class TriangleUnderlayRenderer(QObject): def __init__(self, parent=None): super(TriangleUnderlayRenderer, self).__init__(parent) self._shader_program = None self._viewport_size = QSize() self._window = None @pyqtSlot() def paint(self): # TODO test on Ubuntu # for Darwin, it's a must gl = self._window.openglContext().versionFunctions() if self._shader_program is None: self._shader_program = QOpenGLShaderProgram() self._shader_program.addShaderFromSourceFile( QOpenGLShader.Vertex, 'shaders/OpenGL_2_1/vertex.glsl') self._shader_program.addShaderFromSourceFile( QOpenGLShader.Fragment, 'shaders/OpenGL_2_1/fragment.glsl') self._shader_program.bindAttributeLocation('position', 0) self._shader_program.bindAttributeLocation('color', 1) self._shader_program.link() self._shader_program.bind() self._shader_program.enableAttributeArray(0) self._shader_program.enableAttributeArray(1) self._shader_program.setAttributeArray(0, positions) self._shader_program.setAttributeArray(1, colors) gl.glViewport(0, 0, self._viewport_size.width(), self._viewport_size.height()) gl.glClearColor(0.5, 0.5, 0.5, 1) gl.glDisable(gl.GL_DEPTH_TEST) gl.glClear(gl.GL_COLOR_BUFFER_BIT) gl.glDrawArrays(gl.GL_TRIANGLES, 0, 3) self._shader_program.disableAttributeArray(0) self._shader_program.disableAttributeArray(1) self._shader_program.release() # Restore the OpenGL state for QtQuick rendering self._window.resetOpenGLState() self._window.update() def set_viewport_size(self, size): self._viewport_size = size def set_window(self, window): self._window = window
class GLWidget(QGLWidget): clicked = pyqtSignal() PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2) vsrc = """ attribute highp vec4 vertex; attribute mediump vec4 texCoord; varying mediump vec4 texc; uniform mediump mat4 matrix; void main(void) { gl_Position = matrix * vertex; texc = texCoord; } """ fsrc = """ uniform sampler2D texture; varying mediump vec4 texc; void main(void) { gl_FragColor = texture2D(texture, texc.st); } """ coords = (((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)), ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)), ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)), ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1), (-1, +1, -1)), ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1), (+1, -1, -1)), ((-1, -1, +1), (+1, -1, +1), (+1, +1, +1), (-1, +1, +1))) def __init__(self, parent=None, shareWidget=None): super(GLWidget, self).__init__(parent, shareWidget) self.clearColor = Qt.black self.xRot = 0 self.yRot = 0 self.zRot = 0 self.clearColor = QColor() self.lastPos = QPoint() self.program = None def minimumSizeHint(self): return QSize(50, 50) def sizeHint(self): return QSize(200, 200) def rotateBy(self, xAngle, yAngle, zAngle): self.xRot += xAngle self.yRot += yAngle self.zRot += zAngle self.updateGL() def setClearColor(self, color): self.clearColor = color self.updateGL() def initializeGL(self): self.makeObject() glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) vshader = QOpenGLShader(QOpenGLShader.Vertex, self) vshader.compileSourceCode(self.vsrc) fshader = QOpenGLShader(QOpenGLShader.Fragment, self) fshader.compileSourceCode(self.fsrc) self.program = QOpenGLShaderProgram(self) self.program.addShader(vshader) self.program.addShader(fshader) self.program.bindAttributeLocation('vertex', self.PROGRAM_VERTEX_ATTRIBUTE) self.program.bindAttributeLocation('texCoord', self.PROGRAM_TEXCOORD_ATTRIBUTE) self.program.link() self.program.bind() self.program.setUniformValue('texture', 0) self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE) self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE) self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE, self.vertices) self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE, self.texCoords) def paintGL(self): self.qglClearColor(self.clearColor) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) m = QMatrix4x4() m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0) m.translate(0.0, 0.0, -10.0) m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0) m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0) m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0) self.program.setUniformValue('matrix', m) for i in range(6): glBindTexture(GL_TEXTURE_2D, self.textures[i]) glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4) def resizeGL(self, width, height): side = min(width, height) glViewport((width - side) // 2, (height - side) // 2, side, side) def mousePressEvent(self, event): self.lastPos = event.pos() def mouseMoveEvent(self, event): dx = event.x() - self.lastPos.x() dy = event.y() - self.lastPos.y() if event.buttons() & Qt.LeftButton: self.rotateBy(8 * dy, 8 * dx, 0) elif event.buttons() & Qt.RightButton: self.rotateBy(8 * dy, 0, 8 * dx) self.lastPos = event.pos() def mouseReleaseEvent(self, event): self.clicked.emit() def makeObject(self): self.textures = [] self.texCoords = [] self.vertices = [] for i in range(6): self.textures.append( self.bindTexture(QPixmap(':/images/side%d.png' % (i + 1)))) for j in range(4): self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1))) x, y, z = self.coords[i][j] self.vertices.append((0.2 * x, 0.2 * y, 0.2 * z))
class LogoRenderer(): #protected QOpenGLFunctions """docstring for LogoRenderer""" def __init__(self): super(LogoRenderer, self).__init__() self.m_fAngle = None self.m_fScale = None self.vertices = [] self.normals = [] self.program1 = QOpenGLShaderProgram() self.vertexAttr1 = 0 self.normalAttr1 = 0 self.matrixUniform1 = 0 ver = QOpenGLVersionProfile() ver.setVersion(2, 1) cntx = QOpenGLContext.currentContext() #print("QOpenGLContext:", cntx, ver) fmt = cntx.format() fmt.setVersion(2, 1) cntx.setFormat(fmt) self.gl = cntx.versionFunctions(ver) def render(self): self.gl.glDepthMask(True) self.gl.glClearColor(0.5, 0.5, 0.7, 1.0) self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT) self.gl.glTexParameteri(self.gl.GL_TEXTURE_2D, self.gl.GL_TEXTURE_MIN_FILTER, self.gl.GL_LINEAR) self.gl.glTexParameteri(self.gl.GL_TEXTURE_2D, self.gl.GL_TEXTURE_MAG_FILTER, self.gl.GL_LINEAR) self.gl.glFrontFace(self.gl.GL_CW) self.gl.glCullFace(self.gl.GL_FRONT) self.gl.glEnable(self.gl.GL_CULL_FACE) self.gl.glEnable(self.gl.GL_DEPTH_TEST) modelview = QMatrix4x4() modelview.rotate(self.m_fAngle, 0.0, 1.0, 0.0) modelview.rotate(self.m_fAngle, 1.0, 0.0, 0.0) modelview.rotate(self.m_fAngle, 0.0, 0.0, 1.0) modelview.scale(self.m_fScale) modelview.translate(0.0, -0.2, 0.0) self.program1.bind() self.program1.setUniformValue(self.matrixUniform1, modelview) self.paintQtLogo() self.program1.release() self.gl.glDisable(self.gl.GL_DEPTH_TEST) self.gl.glDisable(self.gl.GL_CULL_FACE) self.m_fAngle += 1.0 def initialize(self): #print("initialize.gls") self.gl.initializeOpenGLFunctions() self.gl.glClearColor(0.1, 0.1, 0.2, 1.0) vshader1 = QOpenGLShader(QOpenGLShader.Vertex, self.program1) vsrc1 = str("attribute highp vec4 vertex;\n" "attribute mediump vec3 normal;\n" "uniform mediump mat4 matrix;\n" "varying mediump vec4 color;\n" "void main(void)\n" "{\n" " vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n" " float angle = max(dot(normal, toLight), 0.0);\n" " vec3 col = vec3(0.40, 1.0, 0.0);\n" " color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n" " color = clamp(color, 0.0, 1.0);\n" " gl_Position = matrix * vertex;\n" "}\n") vshader1.compileSourceCode(vsrc1) fshader1 = QOpenGLShader(QOpenGLShader.Fragment, self.program1) fsrc1 = str("varying mediump vec4 color;\n" "void main(void)\n" "{\n" " gl_FragColor = color;\n" "}\n") fshader1.compileSourceCode(fsrc1) self.program1.addShader(vshader1) self.program1.addShader(fshader1) self.program1.link() self.vertexAttr1 = self.program1.attributeLocation("vertex") self.normalAttr1 = self.program1.attributeLocation("normal") self.matrixUniform1 = self.program1.uniformLocation("matrix") self.gl.glTexParameteri(self.gl.GL_TEXTURE_2D, self.gl.GL_TEXTURE_MIN_FILTER, self.gl.GL_LINEAR) self.gl.glTexParameteri(self.gl.GL_TEXTURE_2D, self.gl.GL_TEXTURE_MAG_FILTER, self.gl.GL_LINEAR) self.m_fAngle = 0 self.m_fScale = 1 self.createGeometry() def paintQtLogo(self): self.program1.enableAttributeArray(self.normalAttr1) self.program1.enableAttributeArray(self.vertexAttr1) self.program1.setAttributeArray(self.vertexAttr1, self.vertices) self.program1.setAttributeArray(self.normalAttr1, self.normals) self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, len(self.vertices)) self.program1.disableAttributeArray(self.normalAttr1) self.program1.disableAttributeArray(self.vertexAttr1) def createGeometry(self): self.vertices.clear() self.normals.clear() x1 = +0.06 y1 = -0.14 x2 = +0.14 y2 = -0.06 x3 = +0.08 y3 = +0.00 x4 = +0.30 y4 = +0.22 self.quad(x1, y1, x2, y2, y2, x2, y1, x1) self.quad(x3, y3, x4, y4, y4, x4, y3, x3) self.extrude(x1, y1, x2, y2) self.extrude(x2, y2, y2, x2) self.extrude(y2, x2, y1, x1) self.extrude(y1, x1, x1, y1) self.extrude(x3, y3, x4, y4) self.extrude(x4, y4, y4, x4) self.extrude(y4, x4, y3, x3) NumSectors = 100 for i in range(NumSectors): angle1 = (i * 2 * math.pi) / NumSectors x5 = 0.30 * math.sin(angle1) y5 = 0.30 * math.cos(angle1) x6 = 0.20 * math.sin(angle1) y6 = 0.20 * math.cos(angle1) angle2 = ((i + 1) * 2 * math.pi) / NumSectors x7 = 0.20 * math.sin(angle2) y7 = 0.20 * math.cos(angle2) x8 = 0.30 * math.sin(angle2) y8 = 0.30 * math.cos(angle2) self.quad(x5, y5, x6, y6, x7, y7, x8, y8) self.extrude(x6, y6, x7, y7) self.extrude(x8, y8, x5, y5) for i in range(len(self.vertices)): self.vertices[i] *= 2.0 def quad(self, x1, y1, x2, y2, x3, y3, x4, y4): #print("quad inicio") self.vertices.append(QVector3D(x1, y1, -0.05)) self.vertices.append(QVector3D(x2, y2, -0.05)) self.vertices.append(QVector3D(x4, y4, -0.05)) self.vertices.append(QVector3D(x3, y3, -0.05)) self.vertices.append(QVector3D(x4, y4, -0.05)) self.vertices.append(QVector3D(x2, y2, -0.05)) n = QVector3D.normal(QVector3D(x2 - x1, y2 - y1, 0.0), QVector3D(x4 - x1, y4 - y1, 0.0)) for i in range(6): self.normals.append(n) self.vertices.append(QVector3D(x4, y4, 0.05)) self.vertices.append(QVector3D(x2, y2, 0.05)) self.vertices.append(QVector3D(x1, y1, 0.05)) self.vertices.append(QVector3D(x2, y2, 0.05)) self.vertices.append(QVector3D(x4, y4, 0.05)) self.vertices.append(QVector3D(x3, y3, 0.05)) n = QVector3D.normal(QVector3D(x2 - x4, y2 - y4, 0.0), QVector3D(x1 - x4, y1 - y4, 0.0)) for i in range(6): self.normals.append(n) #print("quad fin") def extrude(self, x1, y1, x2, y2): #print("extrude inicio") self.vertices.append(QVector3D(x1, y1, +0.05)) self.vertices.append(QVector3D(x2, y2, +0.05)) self.vertices.append(QVector3D(x1, y1, -0.05)) self.vertices.append(QVector3D(x2, y2, -0.05)) self.vertices.append(QVector3D(x1, y1, -0.05)) self.vertices.append(QVector3D(x2, y2, +0.05)) n = QVector3D.normal(QVector3D(x2 - x1, y2 - y1, 0.0), QVector3D(0.0, 0.0, -0.1)) for i in range(6): self.normals.append(n)
class MyWidget(QtOpenGL.QGLWidget): def __init__(self, parent=None): super(MyWidget, self).__init__(parent) self.quadCoord = np.array([[-1., -1., 0.], [1., -1., 0.], [1., 1., 0.], [1., 1., 0.], [-1., 1., 0.], [-1., -1., 0.]]) self.quadCoordTex = np.array([[0, 0], [1., 0.], [1., 1.], [1., 1.], [0, 1.], [0, 0]]) def initializeGL(self): GL.glClearColor(1.0, 0.0, 0.0, 1.0) GL.glEnable(GL.GL_BLEND) GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) GL.glClear(GL.GL_COLOR_BUFFER_BIT) print("OpenGL.GL: " + str(GL.glGetString(GL.GL_VERSION))) print("GL.GLSL: " + str(GL.glGetString(GL.GL_SHADING_LANGUAGE_VERSION))) print("OpenGL ATTRIBUTES:\n", ", ".join(d for d in dir(GL) if d.startswith("GL_"))) self.program = QOpenGLShaderProgram() self.program.addShaderFromSourceCode( QOpenGLShader.Vertex, """#version 120 attribute vec2 position; attribute vec2 texcoord; varying vec2 mytexcoord; void main() { gl_Position = vec4(position, 0., 1.0); mytexcoord = texcoord; }""") self.program.addShaderFromSourceCode( QOpenGLShader.Fragment, """#version 120 uniform sampler2D texture; varying vec2 mytexcoord; void main() { gl_FragColor = texture2D(texture,mytexcoord); }""") print(self.program.log()) self.program.link() self.texture = fillTexture2d( np.outer(np.linspace(0, 1, 128), np.ones(128))) def paintGL(self): #GL.glClear(GL.GL_COLOR_BUFFER_BIT) self.program.bind() self.program.enableAttributeArray("position") self.program.enableAttributeArray("texcoord") self.program.setAttributeArray("position", self.quadCoord) self.program.setAttributeArray("texcoord", self.quadCoordTex) GL.glActiveTexture(GL.GL_TEXTURE0) GL.glBindTexture(GL.GL_TEXTURE_2D, self.texture) self.program.setUniformValue("texture", 0) GL.glDrawArrays(GL.GL_TRIANGLES, 0, len(self.quadCoord))
class GLWidget(QOpenGLWidget): clicked = pyqtSignal() PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2) vsrc = """ attribute highp vec4 vertex; attribute mediump vec4 texCoord; varying mediump vec4 texc; uniform mediump mat4 matrix; void main(void) { gl_Position = matrix * vertex; texc = texCoord; } """ fsrc = """ uniform sampler2D texture; varying mediump vec4 texc; void main(void) { gl_FragColor = texture2D(texture, texc.st); } """ coords = ( ((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)), ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)), ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)), ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1), (-1, +1, -1)), ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1), (+1, -1, -1)), ((-1, -1, +1), (+1, -1, +1), (+1, +1, +1), (-1, +1, +1)), ) def __init__(self, parent=None): super(GLWidget, self).__init__(parent) self.clearColor = QColor(Qt.black) self.xRot = 0 self.yRot = 0 self.zRot = 0 self.program = None self.lastPos = QPoint() def minimumSizeHint(self): return QSize(50, 50) def sizeHint(self): return QSize(200, 200) def rotateBy(self, xAngle, yAngle, zAngle): self.xRot += xAngle self.yRot += yAngle self.zRot += zAngle self.update() def setClearColor(self, color): self.clearColor = color self.update() def initializeGL(self): version_profile = QOpenGLVersionProfile() version_profile.setVersion(2, 0) self.gl = self.context().versionFunctions(version_profile) self.gl.initializeOpenGLFunctions() self.makeObject() self.gl.glEnable(self.gl.GL_DEPTH_TEST) self.gl.glEnable(self.gl.GL_CULL_FACE) vshader = QOpenGLShader(QOpenGLShader.Vertex, self) vshader.compileSourceCode(self.vsrc) fshader = QOpenGLShader(QOpenGLShader.Fragment, self) fshader.compileSourceCode(self.fsrc) self.program = QOpenGLShaderProgram() self.program.addShader(vshader) self.program.addShader(fshader) self.program.bindAttributeLocation("vertex", self.PROGRAM_VERTEX_ATTRIBUTE) self.program.bindAttributeLocation("texCoord", self.PROGRAM_TEXCOORD_ATTRIBUTE) self.program.link() self.program.bind() self.program.setUniformValue("texture", 0) self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE) self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE) self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE, self.vertices) self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE, self.texCoords) def paintGL(self): self.gl.glClearColor( self.clearColor.redF(), self.clearColor.greenF(), self.clearColor.blueF(), self.clearColor.alphaF(), ) self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT) m = QMatrix4x4() m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0) m.translate(0.0, 0.0, -10.0) m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0) m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0) m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0) self.program.setUniformValue("matrix", m) for i, texture in enumerate(self.textures): texture.bind() self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, i * 4, 4) def resizeGL(self, width, height): side = min(width, height) self.gl.glViewport((width - side) // 2, (height - side) // 2, side, side) def mousePressEvent(self, event): self.lastPos = event.pos() def mouseMoveEvent(self, event): dx = event.x() - self.lastPos.x() dy = event.y() - self.lastPos.y() if event.buttons() & Qt.LeftButton: self.rotateBy(8 * dy, 8 * dx, 0) elif event.buttons() & Qt.RightButton: self.rotateBy(8 * dy, 0, 8 * dx) self.lastPos = event.pos() def mouseReleaseEvent(self, event): self.clicked.emit() def makeObject(self): self.textures = [] self.texCoords = [] self.vertices = [] root = QFileInfo(__file__).absolutePath() for i in range(6): self.textures.append( QOpenGLTexture( QImage(root + ("/images/side%d.png" % (i + 1))).mirrored())) for j in range(4): self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1))) x, y, z = self.coords[i][j] self.vertices.append((0.2 * x, 0.2 * y, 0.2 * z))
class GLSliceWidget(QtOpenGL.QGLWidget): _dataModelChanged = QtCore.pyqtSignal() def __init__(self, parent=None, interpolation="linear", **kwargs): logger.debug("init") super(GLSliceWidget, self).__init__(parent, **kwargs) self.renderUpdate = True self.parent = parent self.setAcceptDrops(True) self.texture_LUT = None self.setTransform(TransformModel()) self.renderTimer = QtCore.QTimer(self) self.renderTimer.setInterval(50) self.renderTimer.timeout.connect(self.onRenderTimer) self.renderTimer.start() self.dataModel = None self.dataPos = 0 self.slicePos = 0 self.zoom_fac = 1. self.zoom_x = 0.5 self.zoom_y = 0.5 # the center in tex coords self.zoom_cx = 0.5 self.zoom_cy = 0.5 interpolation_vals = ("linear", "nearest") if not interpolation in interpolation_vals: raise KeyError("interpolation = '%s' not defined ,valid: %s" % (interpolation, str(interpolation_vals))) self.interp = (interpolation == "linear") # self.refresh() def setModel(self, dataModel): logger.debug("setModel") self.dataModel = dataModel if self.dataModel: self.transform.setModel(dataModel) self.dataModel._dataSourceChanged.connect(self.dataSourceChanged) self.dataModel._dataPosChanged.connect(self.dataPosChanged) self._dataModelChanged.connect(self.dataModelChanged) self._dataModelChanged.emit() def setTransform(self, transform): self.transform = transform self.transform._transformChanged.connect(self.refresh) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() else: event.ignore() def dropEvent(self, event): for url in event.mimeData().urls(): path = url.toLocalFile().toLocal8Bit().data() if self.dataModel: self.dataModel.loadFromPath(path, prefetchSize=self.N_PREFETCH) else: self.setModel( DataModel.fromPath(path, prefetchSize=self.N_PREFETCH)) def set_colormap(self, name): """arr should be of shape (N,3) and gives the rgb components of the colormap""" try: arr = spimagine.config.__COLORMAPDICT__[name] self.makeCurrent() self.texture_LUT = fillTexture2d(arr.reshape((1, ) + arr.shape), self.texture_LUT, self.interp) except: print("could not load colormap %s" % name) def set_colormap_rgb(self, color=[1., 1., 1.]): self._set_colormap_array(outer(linspace(0, 1., 255), np.array(color))) def _set_colormap_array(self, arr): """arr should be of shape (N,3) and gives the rgb components of the colormap""" self.makeCurrent() self.texture_LUT = fillTexture2d(arr.reshape((1, ) + arr.shape), self.texture_LUT, self.interp) self.refresh() def initializeGL(self): self.resized = True self.output = zeros((100, 100)) logger.debug("initializeGL") self.programTex = QOpenGLShaderProgram() self.programTex.addShaderFromSourceCode(QOpenGLShader.Vertex, vertShaderTex) self.programTex.addShaderFromSourceCode(QOpenGLShader.Fragment, fragShaderTex) self.programTex.link() self.programTex.bind() logger.debug("GLSL programTex log:%s", self.programTex.log()) glClearColor(0, 0, 0, 1.) self.texture = None self.quadCoord = np.array([[-1., -1., 0.], [1., -1., 0.], [1., 1., 0.], [1., 1., 0.], [-1., 1., 0.], [-1., -1., 0.]]) self.quadCoordTex = np.array([[0, 0], [1., 0.], [1., 1.], [1., 1.], [0, 1.], [0, 0]]) self.set_colormap(spimagine.config.__DEFAULTCOLORMAP__) glDisable(GL_DEPTH_TEST) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) def dataModelChanged(self): # if self.dataModel: # self.transform.reset(amin(self.dataModel[0]), # amax(self.dataModel[0]), # self.dataModel.stackUnits()) # self.refresh() def dataSourceChanged(self): # self.transform.reset(amin(self.dataModel[0]), # amax(self.dataModel[0]), # self.dataModel.stackUnits()) self.refresh() def dataPosChanged(self, pos): self.dataPos = pos self.refresh() def refresh(self): # if self.parentWidget() and self.dataModel: # self.parentWidget().setWindowTitle("SpImagine %s"%self.dataModel.name()) self.renderUpdate = True def resizeGL(self, width, height): # height = max(10,height) self._viewport_width, self._viewport_height = width, height self.resized = True self.resetViewPort() def getDataWidthHeight(self): if not self.dataModel: return 1, 1 dim = array(self.dataModel.size()[1:])[::-1].astype(np.float32) dim *= array(self.transform.stackUnits) if self.transform.sliceDim == 0: dim = dim[[2, 1]] elif self.transform.sliceDim == 1: dim = dim[[0, 2]] elif self.transform.sliceDim == 2: dim = dim[[0, 1]] w, h = dim[0], dim[1] fac = min(1. * self._viewport_width / w, 1. * self._viewport_height / h) return int(fac * w), int(fac * h) def resetViewPort(self): w, h = self.getDataWidthHeight() glViewport((self._viewport_width - w) // 2, (self._viewport_height - h) // 2, w, h) def paintGL(self): self.makeCurrent() if not glCheckFramebufferStatus( GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE: return # hack if self.resized: self.resetViewPort() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) if self.dataModel: # Draw the render texture self.programTex.bind() self.texture = fillTexture2d(self.output, self.texture, self.interp) glEnable(GL_TEXTURE_2D) glDisable(GL_DEPTH_TEST) self.programTex.enableAttributeArray("position") self.programTex.enableAttributeArray("texcoord") self.programTex.setAttributeArray("position", self.quadCoord) self.programTex.setAttributeArray( "texcoord", self.tex_coords_from_xyzoom(self.zoom_x, self.zoom_y, self.zoom_fac)) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.texture) self.programTex.setUniformValue("texture", 0) glActiveTexture(GL_TEXTURE1) glBindTexture(GL_TEXTURE_2D, self.texture_LUT) self.programTex.setUniformValue("texture_LUT", 1) glDrawArrays(GL_TRIANGLES, 0, len(self.quadCoord)) def render(self): logger.debug("render") if self.dataModel: if self.transform.sliceDim == 0: out = fliplr(self.dataModel[self.transform.dataPos] [:, :, self.transform.slicePos].T) elif self.transform.sliceDim == 1: out = self.dataModel[ self.transform.dataPos][:, self.transform.slicePos, :] elif self.transform.sliceDim == 2: out = self.dataModel[self.transform.dataPos][ self.transform.slicePos, :, :] min_out, max_out = self.transform.minVal, self.transform.maxVal if max_out > min_out: self.output = np.maximum( 0, (1. * (out - min_out) / (max_out - min_out)))**self.transform.gamma else: self.output = np.zeros_like(out) logger.debug( "render: output range = %s" % ([amin(self.output), amax(self.output)])) def saveFrame(self, fName): """FIXME: scaling behaviour still hast to be implemented (e.g. after setGamma)""" logger.info("saving frame as %s", fName) self.render() self.paintGL() glFlush() self.grabFrameBuffer().save(fName) def onRenderTimer(self): if self.renderUpdate: self.render() self.renderUpdate = False self.updateGL() def tex_coords_from_xyzoom(self, x0, y0, zoom): """returns array of texccords corners when zoomed in onto x0,y0 \in [0,1] zoom == 1 --> fully zoomed out zoom == 0 --> fully zoomed in (to x0,y0) """ q0 = create_quad_coords([0, 1, 0, 1]) q1 = create_quad_coords([x0, x0, y0, y0]) return zoom * q0 + (1. - zoom) * q1 def getRelativeCoords(self, x0, y0): w, h = self.getDataWidthHeight() w = w * self.width() / self._viewport_width h = w * self.height() / self._viewport_height x = 2. * (x0 - .5 * (self.width() - w)) / w - 1 y = 2. * (y0 - .5 * (self.height() - h)) / h - 1 x = (x0 - .5 * (self.width() - w)) / w y = 1 - (y0 - .5 * (self.height() - h)) / h return x, y def mousePressEvent(self, event): super(GLSliceWidget, self).mousePressEvent(event) if event.buttons() == QtCore.Qt.LeftButton: self._x0, self._y0 = self.getRelativeCoords(event.x(), event.y()) def mouseMoveEvent(self, event): if event.buttons() == QtCore.Qt.LeftButton: x, y = self.getRelativeCoords(event.x(), event.y()) self.zoom_x += self.zoom_fac * (self._x0 - x) self.zoom_y += self.zoom_fac * (self._y0 - y) self._x0, self._y0 = x, y self.zoom_x, self.zoom_y = clip(self.zoom_x, 0, 1), clip(self.zoom_y, 0, 1) self.refresh() def wheelEvent(self, event): # get the zoom factor # print self.zoom_x,self.zoom_y # x, y = self.getRelativeCoords(event.x(),event.y()) # self.zoom_x , self.zoom_y = clip(x ,0,1), clip(y,0,1) self.zoom_fac *= 1.4**(-event.angleDelta().y() / 1000.) self.zoom_fac = clip(self.zoom_fac, 0, 1.) self.refresh()
class GLWidget(QOpenGLWidget): clicked = pyqtSignal() PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2) vsrc = """ attribute highp vec4 vertex; attribute mediump vec4 texCoord; varying mediump vec4 texc; uniform mediump mat4 matrix; void main(void) { gl_Position = matrix * vertex; texc = texCoord; } """ fsrc = """ uniform sampler2D texture; varying mediump vec4 texc; void main(void) { gl_FragColor = texture2D(texture, texc.st); } """ coords = (((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)), ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)), ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)), ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1), (-1, +1, -1)), ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1), (+1, -1, -1)), ((-1, -1, +1), (+1, -1, +1), (+1, +1, +1), (-1, +1, +1))) def __init__(self, parent=None): super(GLWidget, self).__init__(parent) self.clearColor = QColor(Qt.black) self.xRot = 0 self.yRot = 0 self.zRot = 0 self.program = None self.lastPos = QPoint() def minimumSizeHint(self): """ Define the minimum size of the widget """ return QSize(50, 50) def sizeHint(self): """ Define a default size for the widget """ return QSize(200, 200) def rotateBy(self, xAngle, yAngle, zAngle): self.xRot += xAngle self.yRot += yAngle self.zRot += zAngle self.update() def setClearColor(self, color): self.clearColor = color self.update() def initializeGL(self): self.gl = self.context().versionFunctions() self.gl.initializeOpenGLFunctions() self.makeObject() self.gl.glEnable(self.gl.GL_DEPTH_TEST) self.gl.glEnable(self.gl.GL_CULL_FACE) vshader = QOpenGLShader(QOpenGLShader.Vertex, self) vshader.compileSourceCode(self.vsrc) fshader = QOpenGLShader(QOpenGLShader.Fragment, self) fshader.compileSourceCode(self.fsrc) self.program = QOpenGLShaderProgram() self.program.addShader(vshader) self.program.addShader(fshader) self.program.bindAttributeLocation('vertex', self.PROGRAM_VERTEX_ATTRIBUTE) self.program.bindAttributeLocation('texCoord', self.PROGRAM_TEXCOORD_ATTRIBUTE) self.program.link() self.program.bind() self.program.setUniformValue('texture', 0) self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE) self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE) self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE, self.vertices) self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE, self.texCoords) def paintGL(self): self.gl.glClearColor(self.clearColor.redF(), self.clearColor.greenF(), self.clearColor.blueF(), self.clearColor.alphaF()) self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT) m = QMatrix4x4() m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0) m.translate(0.0, 0.0, -10.0) self.program.setUniformValue('matrix', m) self.texture.bind() self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, 0, 4) def resizeGL(self, width, height): side = min(width, height) self.gl.glViewport((width - side) // 2, (height - side) // 2, side, side) def makeObject(self): self.texCoords = [(True, True), (False, True), (False, False), (True, False)] self.vertices = [(0.5, -0.5, -0.5), (-0.5, -0.5, -0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5)] my_movie = QImage('/Users/reno/Dropbox/media/cloudy.png') self.texture = QOpenGLTexture(my_movie.mirrored())
class AkiraRenderWindow(OpenGLWindow): def __init__(self): super(AkiraRenderWindow, self).__init__() self.m_program = 0 self.m_frame = 0.0 self.m_vertex = 0 self.m_vertices = [] self.m_color = 0 self.m_colors = [] self.m_offset = 0 self.m_offsets = 0 self.m_vao = None def initialize(self): self.create_shader() self.create_vao() def create_shader(self): self.m_program = QOpenGLShaderProgram(self) self.m_program.addShaderFromSourceFile(QOpenGLShader.Vertex, "shaders/chapter2.vs.glsl") self.m_program.addShaderFromSourceFile(QOpenGLShader.Fragment, "shaders/chapter2.fs.glsl") self.m_program.link() self.m_vertex = self.m_program.attributeLocation("m_vertex") self.m_offset = self.m_program.attributeLocation("m_offset") self.m_color = self.m_program.attributeLocation("m_color") return self def create_vao(self): vertex_array_object = GL.glGenVertexArrays(1) GL.glBindVertexArray(vertex_array_object) vertex_buffer = GL.glGenBuffers(1) GL.glBindBuffer(GL.GL_ARRAY_BUFFER, vertex_buffer) #using PyQt shader program instead GL.glEnableVertexAttribArray(self.m_vertex) GL.glEnableVertexAttribArray(self.m_color) self.m_vao = vertex_array_object return self def render(self): ratio = int(self.devicePixelRatio().real) GL.glViewport(0, 0, self.width()*ratio, self.height()*ratio) GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.m_offsets = QVector4D(sin(self.m_frame/20)*0.5, cos(self.m_frame/20)*0.5, 0.0, 0.0) self.m_vertices = [(0.25, -0.25, 0, 1.0), (-0.25, -0.25, 0, 1.0), (0.25, 0.25, 0, 1.0)] self.m_colors = [(0.0, 0.0, 1.0, 1.0), (1.0, 0.0, 0.0, 1.0), (0.0, 1.0, 0.0, 1.0)] self.m_program.setAttributeValue(self.m_offset, self.m_offsets) self.m_program.setAttributeArray(self.m_vertex, self.m_vertices) self.m_program.setAttributeArray(self.m_color, self.m_colors) self.m_program.bind() GL.glDrawArrays(GL.GL_TRIANGLES, 0, 3) self.m_program.release() self.m_frame += 1
class TriangleUnderlayRenderer(QObject): def __init__(self, parent=None): super(TriangleUnderlayRenderer, self).__init__(parent) self._shader_program = None self._viewport_size = QSize() self._window = None self._camera = Camera() self._perspective_projection_matrix = perspective_projection( 45.0, 4.0 / 3.0, 0.001, 100.0) self._orthographic_projection_matrix = orthographic_projection( 640.0, 480.0, 0.001, 100.0) self._model_matrix = np.identity(4) self._projection_type = 0 self._projection_matrix = self._perspective_projection_matrix self._theta = 0.0 def set_theta(self, theta): self._theta = theta # around y axis def build_rotation_matrix(self): m = np.identity(4) m[0][0] = np.cos(np.radians(self._theta)) m[0][2] = np.sin(np.radians(self._theta)) m[2][0] = -np.sin(np.radians(self._theta)) m[2][2] = np.cos(np.radians(self._theta)) return m @pyqtSlot(int) def setProjectionType(self, t): if t != self._projection_type: self._projection_type = t @pyqtSlot() def paint(self): # for Darwin, it's a must if pf.uname().system == 'Darwin': global GL GL = self._window.openglContext().versionFunctions() w = self._viewport_size.width() h = self._viewport_size.height() GL.glViewport(0, 0, int(w), int(h)) if self._shader_program is None: self._shader_program = QOpenGLShaderProgram() self._shader_program.addShaderFromSourceFile( QOpenGLShader.Vertex, 'shaders/OpenGL_2_1/vertex.glsl') self._shader_program.addShaderFromSourceFile( QOpenGLShader.Fragment, 'shaders/OpenGL_2_1/fragment.glsl') self._shader_program.bindAttributeLocation('position', 0) self._shader_program.bindAttributeLocation('color', 1) self._shader_program.link() self._shader_program.bind() self._shader_program.enableAttributeArray(0) self._shader_program.enableAttributeArray(1) self._shader_program.setAttributeArray(0, positions) self._shader_program.setAttributeArray(1, colors) if self._projection_type == 0: self._projection_matrix = self._perspective_projection_matrix elif self._projection_type == 1: self._projection_matrix = self._orthographic_projection_matrix self._model_matrix = self.build_rotation_matrix() self._shader_program.setUniformValue( 'model_matrix', QMatrix4x4(self._model_matrix.flatten().tolist())) self._shader_program.setUniformValue( 'view_matrix', QMatrix4x4(self._camera.get_view_matrix().flatten().tolist())) self._shader_program.setUniformValue( 'projection_matrix', QMatrix4x4(self._projection_matrix.flatten().tolist())) GL.glClearColor(0.2, 0.2, 0.2, 1) GL.glEnable(GL.GL_DEPTH_TEST) GL.glClear(GL.GL_COLOR_BUFFER_BIT) GL.glDrawArrays(GL.GL_TRIANGLES, 0, 3) self._shader_program.disableAttributeArray(0) self._shader_program.disableAttributeArray(1) self._shader_program.release() # Restore the OpenGL state for QtQuick rendering self._window.resetOpenGLState() self._window.update() def set_viewport_size(self, size): self._viewport_size = size def set_window(self, window): self._window = window def set_projection_matrix(self): # Need to be set every time we change the size of the window self._perspective_projection_matrix = perspective_projection( 45.0, self._viewport_size.width() / self._viewport_size.height(), 0.001, 100.0) self._orthographic_projection_matrix = orthographic_projection( self._viewport_size.width(), self._viewport_size.height(), 0.001, 100.0)
class ModelUnderlayRenderer(QObject): def __init__(self, parent=None): super(ModelUnderlayRenderer, self).__init__(parent) self._cube_shader = None self._sphere_shader = None self._viewport_size = QSize() self._window = None self._camera = Camera() self._perspective_projection_matrix = None self._orthographic_projection_matrix = None self._model_matrix = np.identity(4) self._projection_type = 0 self._projection_matrix = perspective_projection( 45.0, 640.0 / 480.0, 0.001, 1000.0) self._index_buffer = -1 # keep track of the objects in the scene self._cube_model = Cube() self._sphere_model = Sphere() self._models = dict() self._models[self._cube_model] = [] self._models[self._sphere_model] = [] @pyqtSlot() def paint(self): # for Darwin, it's a must if pf.uname().system == 'Darwin': global GL GL = self._window.openglContext().versionFunctions() w = self._viewport_size.width() h = self._viewport_size.height() GL.glViewport(0, 0, int(w), int(h)) GL.glClearColor(0.1, 0.1, 0.1, 1) GL.glEnable(GL.GL_DEPTH_TEST) GL.glClear(GL.GL_COLOR_BUFFER_BIT) GL.glClear(GL.GL_DEPTH_BUFFER_BIT) # # vertices_block = np.vstack((self._cube_model.vertices, self._sphere_model.vertices)) # colors_block = np.vstack((self._cube_model.colors, self._sphere_model)) # # if len(self._objects) > 1: # for v in self._vertices[1:]: # vertices_block = np.vstack((vertices_block, v)) # for idx, c in enumerate(self._colors[1:]): # if not c: # c = [[0.6, 0.6, 0.7] for i in range(len(self._vertices[idx]))] # colors_block = np.vstack((colors_block, c)) # for i in self._indices[1:]: # indices_block = np.hstack((indices_block, i)) view_matrix = np.identity(4) view_matrix[2][3] = -30 if self._cube_shader is None: self._cube_shader = QOpenGLShaderProgram() self._cube_shader.addShaderFromSourceFile( QOpenGLShader.Vertex, 'shaders/OpenGL_2_1/vertex.glsl') self._cube_shader.addShaderFromSourceFile( QOpenGLShader.Fragment, 'shaders/OpenGL_2_1/fragment.glsl') self._cube_shader.bindAttributeLocation('position', 0) self._cube_shader.bindAttributeLocation('color', 1) self._cube_shader.link() self._cube_shader.bind() self._cube_shader.enableAttributeArray(0) self._cube_shader.enableAttributeArray(1) self._cube_shader.setAttributeArray(0, self._cube_model.vertices.tolist()) self._cube_shader.setAttributeArray(1, self._cube_model.colors.tolist()) # view_matrix = self._camera.get_view_matrix() self._cube_shader.setUniformValue( 'view_matrix', QMatrix4x4(view_matrix.flatten().tolist()).transposed()) self._cube_shader.setUniformValue( 'projection_matrix', QMatrix4x4( self._projection_matrix.flatten().tolist()).transposed()) if self._cube_model in self._models.keys(): for entity in self._models[self._cube_model]: m = create_transformation_matrix(entity.position, entity.rotation, entity.scale) self._cube_shader.setUniformValue( 'model_matrix', QMatrix4x4(m.flatten().tolist())) GL.glDrawElements(GL.GL_TRIANGLES, len(self._cube_model.indices), GL.GL_UNSIGNED_INT, self._cube_model.indices.tolist()) self._cube_shader.disableAttributeArray(0) self._cube_shader.disableAttributeArray(1) self._cube_shader.release() if self._sphere_shader is None: self._sphere_shader = QOpenGLShaderProgram() self._sphere_shader.addShaderFromSourceFile( QOpenGLShader.Vertex, 'shaders/OpenGL_2_1/vertex.glsl') self._sphere_shader.addShaderFromSourceFile( QOpenGLShader.Fragment, 'shaders/OpenGL_2_1/fragment.glsl') self._sphere_shader.bindAttributeLocation('position', 0) self._sphere_shader.bindAttributeLocation('color', 1) self._sphere_shader.link() self._sphere_shader.bind() self._sphere_shader.enableAttributeArray(0) self._sphere_shader.enableAttributeArray(1) self._sphere_shader.setAttributeArray( 0, self._sphere_model.vertices.tolist()) self._sphere_shader.setAttributeArray( 1, self._sphere_model.colors.tolist()) self._sphere_shader.setUniformValue( 'view_matrix', QMatrix4x4(view_matrix.flatten().tolist()).transposed()) self._sphere_shader.setUniformValue( 'projection_matrix', QMatrix4x4( self._projection_matrix.flatten().tolist()).transposed()) if self._sphere_model in self._models.keys(): for entity in self._models[self._sphere_model]: m = create_transformation_matrix(entity.position, entity.rotation, entity.scale) self._cube_shader.setUniformValue( 'model_matrix', QMatrix4x4(m.flatten().tolist())) GL.glDrawElements(GL.GL_TRIANGLES, len(self._sphere_model.indices), GL.GL_UNSIGNED_INT, self._sphere_model.indices.tolist()) self._sphere_shader.disableAttributeArray(0) self._sphere_shader.disableAttributeArray(1) self._sphere_shader.release() # def build_rotation_matrix (t): # m = np.identity(4) # m[0][0] = np.cos(np.radians(t)) # m[0][2] = np.sin(np.radians(t)) # m[2][0] = -np.sin(np.radians(t)) # m[2][2] = np.cos(np.radians(t)) # return m # # global theta # theta += 1 # self._model_matrix = build_rotation_matrix(theta) # self._model_matrix[2][3] = -3 # Restore the OpenGL state for QtQuick rendering self._window.resetOpenGLState() self._window.update() def set_viewport_size(self, size): self._viewport_size = size def set_window(self, window): self._window = window def set_projection_matrix(self): # Need to be set every time we change the size of the window self._projection_matrix = perspective_projection( 45.0, self._window.width() / self._window.height(), 0.001, 1000.0) def move_model(self, val): self._model_matrix[2][3] += val def move_camera(self): pass def add_geometry(self, geo_enum): if geo_enum == 0: self._models[self._cube_model].append( Entity( self._cube_model, np.array([ random.uniform(-3.0, 3.0), random.uniform(-3.0, 3.0), random.uniform(-20.0, -10.0) ]), np.array([ random.uniform(-45.0, 45.0), random.uniform(-45.0, 45.0), random.uniform(-45.0, 45.0) ]), np.array([1.0, 1.0, 1.0]))) elif geo_enum == 1: self._models[self._sphere_model].append( Entity( self._sphere_model, np.array([ random.uniform(-3.0, 3.0), random.uniform(-3.0, 3.0), random.uniform(-20.0, -10.0) ]), np.array([ random.uniform(-30.0, 30.0), random.uniform(-30.0, 30.0), random.uniform(-30.0, 30.0) ]), np.array([1.0, 1.0, 1.0]))) else: return def delete_geometry(self, geo_enum): if geo_enum == 0: if self._models[self._cube_model]: self._models[self._cube_model].pop() elif geo_enum == 1: if self._models[self._sphere_model]: self._models[self._sphere_model].pop() def change_random_cube_color(self): tmp = self._models[self._cube_model] self._models.pop(self._cube_model) self._cube_model = Cube() self._models[self._cube_model] = tmp def change_random_sphere_color(self): tmp = self._models[self._sphere_model] self._models.pop(self._sphere_model) self._sphere_model = Sphere() self._models[self._sphere_model] = tmp
class GLSliceWidget(QtOpenGL.QGLWidget): _dataModelChanged = QtCore.pyqtSignal() def __init__(self, parent=None, interpolation="linear", **kwargs): logger.debug("init") super(GLSliceWidget, self).__init__(parent, **kwargs) self.renderUpdate = True self.parent = parent self.setAcceptDrops(True) self.texture_LUT = None self.setTransform(TransformModel()) self.renderTimer = QtCore.QTimer(self) self.renderTimer.setInterval(50) self.renderTimer.timeout.connect(self.onRenderTimer) self.renderTimer.start() self.dataModel = None self.dataPos = 0 self.slicePos = 0 self.zoom_fac = 1. self.zoom_x = 0.5 self.zoom_y = 0.5 # the center in tex coords self.zoom_cx = 0.5 self.zoom_cy = 0.5 interpolation_vals = ("linear", "nearest") if not interpolation in interpolation_vals: raise KeyError("interpolation = '%s' not defined ,valid: %s" % (interpolation, str(interpolation_vals))) self.interp = (interpolation =="linear") # self.refresh() def setModel(self, dataModel): logger.debug("setModel") self.dataModel = dataModel if self.dataModel: self.transform.setModel(dataModel) self.dataModel._dataSourceChanged.connect(self.dataSourceChanged) self.dataModel._dataPosChanged.connect(self.dataPosChanged) self._dataModelChanged.connect(self.dataModelChanged) self._dataModelChanged.emit() def setTransform(self, transform): self.transform = transform self.transform._transformChanged.connect(self.refresh) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() else: event.ignore() def dropEvent(self, event): for url in event.mimeData().urls(): path = url.toLocalFile().toLocal8Bit().data() if self.dataModel: self.dataModel.loadFromPath(path, prefetchSize=self.N_PREFETCH) else: self.setModel(DataModel.fromPath(path, prefetchSize=self.N_PREFETCH)) def set_colormap(self, name): """arr should be of shape (N,3) and gives the rgb components of the colormap""" try: arr = spimagine.config.__COLORMAPDICT__[name] self.makeCurrent() self.texture_LUT = fillTexture2d(arr.reshape((1,) + arr.shape), self.texture_LUT, self.interp) except: print("could not load colormap %s" % name) def set_colormap_rgb(self, color=[1., 1., 1.]): self._set_colormap_array(outer(linspace(0, 1., 255), np.array(color))) def _set_colormap_array(self, arr): """arr should be of shape (N,3) and gives the rgb components of the colormap""" self.makeCurrent() self.texture_LUT = fillTexture2d(arr.reshape((1,) + arr.shape), self.texture_LUT, self.interp) self.refresh() def initializeGL(self): self.resized = True self.output = zeros((100, 100)) logger.debug("initializeGL") self.programTex = QOpenGLShaderProgram() self.programTex.addShaderFromSourceCode(QOpenGLShader.Vertex, vertShaderTex) self.programTex.addShaderFromSourceCode(QOpenGLShader.Fragment, fragShaderTex) self.programTex.link() self.programTex.bind() logger.debug("GLSL programTex log:%s", self.programTex.log()) glClearColor(0, 0, 0, 1.) self.texture = None self.quadCoord = np.array([[-1., -1., 0.], [1., -1., 0.], [1., 1., 0.], [1., 1., 0.], [-1., 1., 0.], [-1., -1., 0.]]) self.quadCoordTex = np.array([[0, 0], [1., 0.], [1., 1.], [1., 1.], [0, 1.], [0, 0]]) self.set_colormap(spimagine.config.__DEFAULTCOLORMAP__) glDisable(GL_DEPTH_TEST) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) def dataModelChanged(self): # if self.dataModel: # self.transform.reset(amin(self.dataModel[0]), # amax(self.dataModel[0]), # self.dataModel.stackUnits()) # self.refresh() def dataSourceChanged(self): # self.transform.reset(amin(self.dataModel[0]), # amax(self.dataModel[0]), # self.dataModel.stackUnits()) self.refresh() def dataPosChanged(self, pos): self.dataPos = pos self.refresh() def refresh(self): # if self.parentWidget() and self.dataModel: # self.parentWidget().setWindowTitle("SpImagine %s"%self.dataModel.name()) self.renderUpdate = True def resizeGL(self, width, height): # height = max(10,height) self._viewport_width, self._viewport_height = width, height self.resized = True self.resetViewPort() def getDataWidthHeight(self): if not self.dataModel: return 1, 1 dim = array(self.dataModel.size()[1:])[::-1].astype(np.float32) dim *= array(self.transform.stackUnits) if self.transform.sliceDim == 0: dim = dim[[2, 1]] elif self.transform.sliceDim == 1: dim = dim[[0, 2]] elif self.transform.sliceDim == 2: dim = dim[[0, 1]] w, h = dim[0], dim[1] fac = min(1. * self._viewport_width / w, 1. * self._viewport_height / h) return int(fac * w), int(fac * h) def resetViewPort(self): w, h = self.getDataWidthHeight() glViewport((self._viewport_width - w) // 2, (self._viewport_height - h) // 2, w, h) def paintGL(self): self.makeCurrent() if not glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE: return # hack if self.resized: self.resetViewPort() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) if self.dataModel: # Draw the render texture self.programTex.bind() self.texture = fillTexture2d(self.output, self.texture, self.interp) glEnable(GL_TEXTURE_2D) glDisable(GL_DEPTH_TEST) self.programTex.enableAttributeArray("position") self.programTex.enableAttributeArray("texcoord") self.programTex.setAttributeArray("position", self.quadCoord) self.programTex.setAttributeArray("texcoord", self.tex_coords_from_xyzoom(self.zoom_x, self.zoom_y, self.zoom_fac)) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.texture) self.programTex.setUniformValue("texture", 0) glActiveTexture(GL_TEXTURE1) glBindTexture(GL_TEXTURE_2D, self.texture_LUT) self.programTex.setUniformValue("texture_LUT", 1) glDrawArrays(GL_TRIANGLES, 0, len(self.quadCoord)) def render(self): logger.debug("render") if self.dataModel: if self.transform.sliceDim == 0: out = fliplr(self.dataModel[self.transform.dataPos][:, :, self.transform.slicePos].T) elif self.transform.sliceDim == 1: out = self.dataModel[self.transform.dataPos][:, self.transform.slicePos, :] elif self.transform.sliceDim == 2: out = self.dataModel[self.transform.dataPos][self.transform.slicePos, :, :] min_out, max_out = self.transform.minVal, self.transform.maxVal if max_out > min_out: self.output = np.maximum(0,(1. * (out - min_out) / (max_out - min_out))) ** self.transform.gamma else: self.output = np.zeros_like(out) logger.debug("render: output range = %s" % ([amin(self.output), amax(self.output)])) def saveFrame(self, fName): """FIXME: scaling behaviour still hast to be implemented (e.g. after setGamma)""" logger.info("saving frame as %s", fName) self.render() self.paintGL() glFlush() self.grabFrameBuffer().save(fName) def onRenderTimer(self): if self.renderUpdate: self.render() self.renderUpdate = False self.updateGL() def tex_coords_from_xyzoom(self, x0, y0, zoom): """returns array of texccords corners when zoomed in onto x0,y0 \in [0,1] zoom == 1 --> fully zoomed out zoom == 0 --> fully zoomed in (to x0,y0) """ q0 = create_quad_coords([0, 1, 0, 1]) q1 = create_quad_coords([x0, x0, y0, y0]) return zoom * q0 + (1. - zoom) * q1 def getRelativeCoords(self, x0, y0): w, h = self.getDataWidthHeight() w = w * self.width() / self._viewport_width h = w * self.height() / self._viewport_height x = 2. * (x0 - .5 * (self.width() - w)) / w - 1 y = 2. * (y0 - .5 * (self.height() - h)) / h - 1 x = (x0 - .5 * (self.width() - w)) / w y = 1 - (y0 - .5 * (self.height() - h)) / h return x, y def mousePressEvent(self, event): super(GLSliceWidget, self).mousePressEvent(event) if event.buttons() == QtCore.Qt.LeftButton: self._x0, self._y0 = self.getRelativeCoords(event.x(), event.y()) def mouseMoveEvent(self, event): if event.buttons() == QtCore.Qt.LeftButton: x, y = self.getRelativeCoords(event.x(), event.y()) self.zoom_x += self.zoom_fac * (self._x0 - x) self.zoom_y += self.zoom_fac * (self._y0 - y) self._x0, self._y0 = x, y self.zoom_x, self.zoom_y = clip(self.zoom_x, 0, 1), clip(self.zoom_y, 0, 1) self.refresh() def wheelEvent(self, event): # get the zoom factor # print self.zoom_x,self.zoom_y # x, y = self.getRelativeCoords(event.x(),event.y()) # self.zoom_x , self.zoom_y = clip(x ,0,1), clip(y,0,1) self.zoom_fac *= 1.4 ** (-event.angleDelta().y() / 1000.) self.zoom_fac = clip(self.zoom_fac, 0, 1.) self.refresh()
class MyWidget(QtOpenGL.QGLWidget): def __init__(self, parent = None): super(MyWidget, self).__init__(parent) self.quadCoord = np.array([[-1., -1., 0.], [1., -1., 0.], [1., 1., 0.], [1., 1., 0.], [-1., 1., 0.], [-1., -1., 0.]]) self.quadCoordTex = np.array([[0, 0], [1., 0.], [1., 1.], [1., 1.], [0, 1.], [0, 0]]) def initializeGL(self): GL.glClearColor(1.0, 0.0, 0.0, 1.0) GL.glEnable(GL.GL_BLEND) GL.glBlendFunc (GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) GL.glClear(GL.GL_COLOR_BUFFER_BIT) print("OpenGL.GL: " + str(GL.glGetString(GL.GL_VERSION))) print("GL.GLSL: " + str(GL.glGetString(GL.GL_SHADING_LANGUAGE_VERSION))) print("OpenGL ATTRIBUTES:\n",", ".join(d for d in dir(GL) if d.startswith("GL_"))) self.program = QOpenGLShaderProgram() self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, """#version 120 attribute vec2 position; attribute vec2 texcoord; varying vec2 mytexcoord; void main() { gl_Position = vec4(position, 0., 1.0); mytexcoord = texcoord; }""") self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, """#version 120 uniform sampler2D texture; varying vec2 mytexcoord; void main() { gl_FragColor = texture2D(texture,mytexcoord); }""") print(self.program.log()) self.program.link() self.texture = fillTexture2d(np.outer(np.linspace(0, 1, 128), np.ones(128))) def paintGL(self): #GL.glClear(GL.GL_COLOR_BUFFER_BIT) self.program.bind() self.program.enableAttributeArray("position") self.program.enableAttributeArray("texcoord") self.program.setAttributeArray("position", self.quadCoord) self.program.setAttributeArray("texcoord", self.quadCoordTex) GL.glActiveTexture(GL.GL_TEXTURE0) GL.glBindTexture(GL.GL_TEXTURE_2D, self.texture) self.program.setUniformValue("texture", 0) GL.glDrawArrays(GL.GL_TRIANGLES, 0, len(self.quadCoord))
class GLWidget(QOpenGLWidget): clicked = pyqtSignal() PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2) vsrc = """ attribute highp vec4 vertex; attribute mediump vec4 texCoord; varying mediump vec4 texc; uniform mediump mat4 matrix; void main(void) { gl_Position = matrix * vertex; texc = texCoord; } """ fsrc = """ uniform sampler2D texture; varying mediump vec4 texc; void main(void) { gl_FragColor = texture2D(texture, texc.st); } """ coords = ( (( +1, -1, -1 ), ( -1, -1, -1 ), ( -1, +1, -1 ), ( +1, +1, -1 )), (( +1, +1, -1 ), ( -1, +1, -1 ), ( -1, +1, +1 ), ( +1, +1, +1 )), (( +1, -1, +1 ), ( +1, -1, -1 ), ( +1, +1, -1 ), ( +1, +1, +1 )), (( -1, -1, -1 ), ( -1, -1, +1 ), ( -1, +1, +1 ), ( -1, +1, -1 )), (( +1, -1, +1 ), ( -1, -1, +1 ), ( -1, -1, -1 ), ( +1, -1, -1 )), (( -1, -1, +1 ), ( +1, -1, +1 ), ( +1, +1, +1 ), ( -1, +1, +1 )) ) def __init__(self, parent=None): super(GLWidget, self).__init__(parent) self.clearColor = QColor(Qt.black) self.xRot = 0 self.yRot = 0 self.zRot = 0 self.program = None self.lastPos = QPoint() def minimumSizeHint(self): return QSize(50, 50) def sizeHint(self): return QSize(200, 200) def rotateBy(self, xAngle, yAngle, zAngle): self.xRot += xAngle self.yRot += yAngle self.zRot += zAngle self.update() def setClearColor(self, color): self.clearColor = color self.update() def initializeGL(self): self.gl = self.context().versionFunctions() self.gl.initializeOpenGLFunctions() self.makeObject() self.gl.glEnable(self.gl.GL_DEPTH_TEST) self.gl.glEnable(self.gl.GL_CULL_FACE) vshader = QOpenGLShader(QOpenGLShader.Vertex, self) vshader.compileSourceCode(self.vsrc) fshader = QOpenGLShader(QOpenGLShader.Fragment, self) fshader.compileSourceCode(self.fsrc) self.program = QOpenGLShaderProgram() self.program.addShader(vshader) self.program.addShader(fshader) self.program.bindAttributeLocation('vertex', self.PROGRAM_VERTEX_ATTRIBUTE) self.program.bindAttributeLocation('texCoord', self.PROGRAM_TEXCOORD_ATTRIBUTE) self.program.link() self.program.bind() self.program.setUniformValue('texture', 0) self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE) self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE) self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE, self.vertices) self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE, self.texCoords) def paintGL(self): self.gl.glClearColor(self.clearColor.redF(), self.clearColor.greenF(), self.clearColor.blueF(), self.clearColor.alphaF()) self.gl.glClear( self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT) m = QMatrix4x4() m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0) m.translate(0.0, 0.0, -10.0) m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0) m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0) m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0) self.program.setUniformValue('matrix', m) for i, texture in enumerate(self.textures): texture.bind() self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, i * 4, 4) def resizeGL(self, width, height): side = min(width, height) self.gl.glViewport((width - side) // 2, (height - side) // 2, side, side) def mousePressEvent(self, event): self.lastPos = event.pos() def mouseMoveEvent(self, event): dx = event.x() - self.lastPos.x() dy = event.y() - self.lastPos.y() if event.buttons() & Qt.LeftButton: self.rotateBy(8 * dy, 8 * dx, 0) elif event.buttons() & Qt.RightButton: self.rotateBy(8 * dy, 0, 8 * dx) self.lastPos = event.pos() def mouseReleaseEvent(self, event): self.clicked.emit() def makeObject(self): self.textures = [] self.texCoords = [] self.vertices = [] root = QFileInfo(__file__).absolutePath() for i in range(6): self.textures.append( QOpenGLTexture( QImage(root + ('/images/side%d.png' % (i + 1))).mirrored())) for j in range(4): self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1))) x, y, z = self.coords[i][j] self.vertices.append((0.2 * x, 0.2 * y, 0.2 * z))
class SquircleRenderer(QObject): #QOpenGLFunctions """docstring for SquircleRenderer""" def __init__(self, parent=None): super(SquircleRenderer, self).__init__(parent) self.m_t = 0.0 self.m_program = None self.m_viewportSize = QSize() def setT(self, t): self.m_t = t def setViewportSize(self, size): self.m_viewportSize = size def setWin(self, win): self.win = win ver = QOpenGLVersionProfile() ver.setVersion(2, 1) self.m_context = self.win.openglContext() self.gl = self.m_context.versionFunctions(ver) @pyqtSlot() def paint(self): if not self.m_program: self.gl.initializeOpenGLFunctions() self.m_program = QOpenGLShaderProgram(self) self.m_program.addShaderFromSourceCode( QOpenGLShader.Vertex, "attribute highp vec4 vertices;" "varying highp vec2 coords;" "void main() {" " gl_Position = vertices;" " coords = vertices.xy;" "}") self.m_program.addShaderFromSourceCode( QOpenGLShader.Fragment, "uniform lowp float t;" "varying highp vec2 coords;" "void main() {" " lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));" " i = smoothstep(t - 0.8, t + 0.8, i);" " i = floor(i * 20.) / 20.;" " gl_FragColor = vec4(coords * .5 + .5, i, i);" "}") self.m_program.bindAttributeLocation("vertices", 0) self.m_program.link() self.m_program.bind() self.m_program.enableAttributeArray(0) values = [(-1, -1), (1, -1), (-1, 1), (1, 1)] self.m_program.setAttributeArray(0, values) self.m_program.setUniformValue("t", self.m_t) #print("DATA:",self.m_viewportSize.width(), self.m_viewportSize.height(), self.m_t)#, self.gl.glViewport) self.gl.glViewport(0, 0, self.m_viewportSize.width(), self.m_viewportSize.height()) self.gl.glDisable(self.gl.GL_DEPTH_TEST) self.gl.glClearColor(0, 0, 0, 1) self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT) self.gl.glEnable(self.gl.GL_BLEND) self.gl.glBlendFunc(self.gl.GL_SRC_ALPHA, self.gl.GL_ONE) self.gl.glDrawArrays(self.gl.GL_TRIANGLE_STRIP, 0, 4) self.m_program.disableAttributeArray(0) self.m_program.release()
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, processor, df, parent=None): super().__init__(parent) self.parent = parent self.processor = processor # Data self.df = df # UI self.setupUi(self) self.setupControls() self.keyPressEvent = self.keyPressed self.mouseMoveEvent = self.mouseMoved # Control & Display self.mouse_grabbed = False self.camera_pos = QVector3D(0.5, 0.5, -2) # Start camera position self.center = QVector3D(0.5, 0, 0.5) # Center of object self.rot_center = QVector3D(0.5, 0.5, 0.5) # Center of rotation self.camera_rot = QVector3D(0, 0, 1) # Camera rotation self.scale_vec = QVector3D(1, 1, 1) # Object scale self.real_prop = processor.get_real_scaling() # val to lat self.light_pos = QVector3D(self.xLightSpinBox.value(), self.yLightSpinBox.value(), self.zLightSpinBox.value()) self.ambient = self.ambientSlider.value() / 100 self.diffuse = self.diffuseSlider.value() / 100 self.alpha = self.alphaSlider.value() / 100 # Transparency # Drawing self.normals = [] self.colors = [] self.coords_array = [] # !> len(self.normals) == len(self.colors) == len(self.coords_array) self.update_light = False # Update for light is needed self.update_buffer = False # Update for whole buffer is needed self.show_grid = self.gridCheckBox.isChecked() self.show_contour = self.contourCheckBox.isChecked() self.contour_levels = self.contourLevelSpinBox.value() self.show_light_lines = True self.grid_freq = 10 self.grid_color = QVector4D(1, 1, 1, 1) self.contour_color = QVector4D(1, 1, 1, 1) self.light_line_color = QVector4D(1, 0.6, 0, 1) self.prepareScene() self.updateUi() self.shaders = QOpenGLShaderProgram() self.openGLWidget.initializeGL = self.initializeGL self.openGLWidget.paintGL = self.paintGL def updateUi(self): """Set up custom widgets & positions""" self.splitDockWidget(self.displayDockWidget, self.elevationDockWidget, Qt.Vertical) self.splitDockWidget(self.elevationDockWidget, self.displayDockWidget, Qt.Vertical) self.tabifyDockWidget(self.elevationDockWidget, self.minimapDockWidget) self.tabifyDockWidget(self.displayDockWidget, self.lightDockWidget) self.tabifyDockWidget(self.projDockWidget, self.additionalDockWidget) self.tabifyDockWidget(self.elevationDockWidget, self.cameraDockWidget) self.lightDockWidget.raise_() self.additionalDockWidget.raise_() self.elevationDockWidget.raise_() self.elevationWidget = ElevationGraphWidget( self.processor.min_val, self.processor.max_val, self.processor.denormalizeValue(self.camera_pos.y()), width=240, height=100) self.minimapWidget = MinimapGraphWidget(self.processor, self.camera_pos, self.camera_rot, width=240, height=100) self.elevationWidgetLayout.addWidget(self.elevationWidget) self.minimapLayout.addWidget(self.minimapWidget) self.mapDockWidgetControls() self.actionOpenAnother.triggered.connect(self.onOpenAnother) def onOpenAnother(self): """Open another GeoTIFF file""" self.parent.show() self.hide() self.deleteLater() def mapDockWidgetControls(self): """Show/hide dockwidgets via corresponding actions""" self.dock_widgets = [ self.lightDockWidget, self.cameraDockWidget, self.additionalDockWidget, self.minimapDockWidget, self.displayDockWidget, self.projDockWidget, self.elevationDockWidget ] self.dock_actions = [ self.actionShowLightSourceDW, self.actionShowCameraDW, self.actionShowAdditionalDW, self.actionShowMinimapDW, self.actionShowDisplayDW, self.actionShowProjectionDW, self.actionShowElevationDW ] for dock_widget, action in zip(self.dock_widgets, self.dock_actions): def wrapper(action): def dock_widget_close_event(event): action.setChecked(False) event.accept() return dock_widget_close_event dock_widget.closeEvent = wrapper(action) action.triggered.connect(dock_widget.setVisible) # ==================== PREPARATION ==================== def prepareScene(self): """Prepare the data for bufferization""" self.coords_array = [] self.colors = [] self.normals = [] polygons, normals, colors = self.getMapPolygons() self.map_data = self.preparePolygons(polygons, normals, colors) # self.normal_data = self.prepareNormalLines(polygons, normals, colors) polygons, normals, colors = self.getLightSourceCoords() self.light_data = self.preparePolygons(polygons, normals, colors) if self.show_light_lines: lines, line_colors = self.getLightLines() self.light_lines_data = self.prepareLines(lines, line_colors) if self.show_grid: lines, line_colors = self.getGrid() self.grid_data = self.prepareLines(lines, line_colors) if self.show_contour: self.contour_data = self.getContour() # POLYGONS def getLightSourceCoords(self): polygons = np.array([((0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 0, 0)), ((0, 0, 1), (0, 1, 1), (1, 1, 1), (1, 0, 1)), ((0, 0, 0), (0, 0, 1), (1, 0, 1), (1, 0, 0)), ((0, 1, 0), (0, 1, 1), (1, 1, 1), (1, 1, 0)), ((0, 0, 0), (0, 0, 1), (0, 1, 1), (0, 1, 0)), ((1, 0, 0), (1, 0, 1), (1, 1, 1), (1, 1, 0))]) normals = [(0, 0, -1), (0, 0, 1), (0, -1, 0), (0, 1, 0), (-1, 0, 0), (1, 0, 0)] normals_vec = [] [[ normals_vec.append(QVector3D(*normals[i])) for _ in range(len(polygons[i])) ] for i in range(len(polygons))] center = np.array( (self.light_pos.x(), self.light_pos.y(), self.light_pos.z())) delta = np.array((0.5, 0.5, 0.5)) polygons = [[(p - delta) * 0.05 + center for p in side] for side in polygons] colors = [] [ colors.append(QVector4D(1, 153 / 255, 0, 1) * self.diffuse) for _ in range(len(normals_vec)) ] return polygons, normals_vec, colors def getMapPolygons(self): polygons, normals, colors = [], [], [] for polygon, normal in self.processor.polygon_generator(self.df): polygons.append(self.swapPoints(polygon)) [ normals.append(QVector3D(*self.swapPoint(*normal))) for _ in polygon ] [colors.append(self.getColorByValue(val)) for x, y, val in polygon] return polygons, normals, colors def getColorByValue(self, value): """Get QVector4D-color from green (value == 0) to red (value == 1) :param value: 0 - 1 """ hue = 120 * (1 - value) / 360 color = QColor.fromHslF(hue, 1, 0.5) color_vec = QVector4D(color.redF(), color.greenF(), color.blueF(), 0.5) return color_vec def swapPoint(self, lon, lat, value): return lon, value, lat def swapPoints(self, polygon): return [(lon, value, lat) for lon, lat, value in polygon] def prepareNormalLines(self, polygons, normals, colors): """Normal lines for each polygon. Debug only """ norm_i = 0 start = len(self.coords_array) for polygon in polygons: point = polygon[0] normal = normals[norm_i] # color = colors[norm_i] color = QVector4D(1, 1, 1, 1) point_2 = QVector3D(*point) + normal * 0.04 point_2 = (point_2.x(), point_2.y(), point_2.z()) self.prepareLine((point, point_2), [color] * 2) norm_i += len(polygon) end = len(self.coords_array) return start, end def preparePolygons(self, polygons, normals, colors, start_index=None): """Prepare polygons for bufferization :param polygons: a list like [(p11, p12, ...), (p21, p22, ...)] :param normals: normals with number of normals equal to points :param colors: colors for each point :param start_index: if None, start at the end of each array. Otherwise, overwrite """ assert len(normals) == len(colors) coords_array = [] [[coords_array.append(list(p)) for p in polygon] for polygon in polygons] start = len(self.coords_array) if start_index is None: self.coords_array += coords_array self.normals += normals self.colors += colors else: for i, j in enumerate( range(start_index, start_index + len(coords_array))): self.coords_array[j] = coords_array[i] self.normals[j] = normals[i] self.colors[j] = colors[i] end = len(self.coords_array) return start, end # LINES def getGrid(self): assert self.processor.min_lat != self.processor.max_lat \ and self.processor.min_val != self.processor.max_val value = self.processor.min_val - \ (self.processor.max_val - self.processor.min_val) * 0.1 lines = [] for lat in np.linspace(self.processor.min_lat, self.processor.max_lat, self.grid_freq): line = ((self.processor.min_lon, lat, value), (self.processor.max_lon, lat, value)) lines.append(line) for lon in np.linspace(self.processor.min_lon, self.processor.max_lon, self.grid_freq): line = ((lon, self.processor.min_lat, value), (lon, self.processor.max_lat, value)) lines.append(line) # lines.append(((self.min_lon, self.min_lat, self.min_val), # (self.min_lon, self.min_lat, self.max_val))) lines = [ self.swapPoints(self.processor.normalizePoints(line)) for line in lines ] line_colors = [(self.grid_color, self.grid_color) for _ in lines] return lines, line_colors def getContour(self): lev_lines = self.processor.get_contour(levels=self.contour_levels) contour = [] for level, line in lev_lines: line = [(self.processor.normalizeLon(lon), self.processor.normalizeValue(level + 10), self.processor.normalizeLat(lat)) for lon, lat in line] colors = [self.contour_color] * len(line) contour.append(self.prepareLine(line, colors)) return contour def getLightLines(self): if self.processor.max_val == self.processor.min_val: v = 0 else: v = self.processor.normalizeValue( self.processor.min_val - (self.processor.max_val - self.processor.min_val) * 0.1) lines = (((self.light_pos.x(), v, -100), (self.light_pos.x(), v, 100)), ((-100, v, self.light_pos.z()), (100, v, self.light_pos.z())), ((self.light_pos.x(), v, self.light_pos.z()), (self.light_pos.x(), self.light_pos.y(), self.light_pos.z()))) line_colors = [(self.light_line_color, self.light_line_color) for _ in lines] return lines, line_colors def prepareLines(self, lines, line_colors, start_index=None): """Prepare lines for bufferization :param lines: list like one for polygons :param line_colors: list of colors of each line :param start_index: start index """ assert len(lines) == len(line_colors) if start_index is None: data = [None] * len(lines) else: data = [] sum_len = 0 for line in lines: data.append(start_index + sum_len) sum_len += len(line) result = [ self.prepareLine(line, colors, datum) for line, colors, datum in zip(lines, line_colors, data) ] return result[0][0], result[-1][1] def prepareLine(self, line, colors, start_index=None): assert len(line) == len(colors) if start_index is None: start = len(self.coords_array) self.coords_array += line self.colors += colors self.normals += [QVector3D(0, 1, 0)] * len(line) end = len(self.coords_array) else: start = start_index for i, j in enumerate(range(start_index, start_index + len(line))): self.coords_array[j] = line[i] self.colors[j] = colors[i] end = start_index + len(line) return start, end # ==================== SCENE PREPARATION ==================== def initializeGL(self): GL.glClearColor(0.1, 0.1, 0.1, 1.0) GL.glEnableClientState(GL.GL_VERTEX_ARRAY) self.setUpShaders() self.initVertexArrays() def setUpShaders(self): self.shaders.addShaderFromSourceFile( QOpenGLShader.Vertex, resource_path('shaders/shader.vert')) self.shaders.addShaderFromSourceFile( QOpenGLShader.Fragment, resource_path('shaders/shader.frag')) self.shaders.link() self.shaders.bind() def initVertexArrays(self): """Init buffers""" assert len(self.coords_array) == len(self.colors) == len(self.normals) GL.glVertexPointer(3, GL.GL_FLOAT, 0, self.coords_array) self.shaders.setAttributeArray("v_color", self.colors) self.shaders.enableAttributeArray("v_color") self.shaders.setAttributeArray("v_normal", self.normals) self.shaders.enableAttributeArray("v_normal") # ==================== UPDATING STUFF ==================== def updateGL(func): def wrapper(self, *args, **kwargs): res = func(self, *args, **kwargs) self.openGLWidget.update() return res return wrapper def updateCameraInfo(func): """A decorator to update camera widgets after excution """ def wrapper(self, *args, **kwargs): res = func(self, *args, **kwargs) coef = 1 if not self.realPropCheckBox.isChecked() \ else self.real_prop self.elevationWidget.updatePos( self.processor.denormalizeValue(self.camera_pos.y() / (self.scale_vec.y()) * coef)) self.minimapWidget.updateCameraInfo( self.camera_pos * self.scale_vec, self.camera_rot) return res return wrapper def updateLightData(self): """Update parts of buffer used to display the light source""" polygons, normals, colors = self.getLightSourceCoords() self.preparePolygons(polygons, normals, colors, self.light_data[0]) lines, colors = self.getLightLines() self.prepareLines(lines, colors, self.light_lines_data[0]) GL.glVertexPointer(3, GL.GL_FLOAT, 0, self.coords_array) self.update_light = False def updateBuffer(self): """Update the whole buffer""" self.prepareScene() self.initVertexArrays() self.update_buffer = False # ==================== ACTUAL DRAWING ==================== def paintGL(self): self.loadScene() if self.update_light: self.updateLightData() if self.update_buffer: self.updateBuffer() self.updateMatrices() self.updateParams() self.drawScene() def loadScene(self): width, height = self.openGLWidget.width(), self.openGLWidget.height() view = max(width, height) GL.glViewport(int((width - view) / 2), int((height - view) / 2), view, view) GL.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL) GL.glEnable(GL.GL_BLEND) GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA) GL.glEnable(GL.GL_DEPTH_TEST) GL.glDepthFunc(GL.GL_LEQUAL) def updateMatrices(self): """Update projection matrices""" proj = QMatrix4x4() coef = 0.01 center_x = 0 center_y = 0 projection = (-1 * coef + center_x, 1 * coef + center_x, -1 * coef + center_y, 1 * coef + center_y, 2.8 * coef, 20) if self.perspectiveRadioButton.isChecked(): proj.frustum(*projection) else: proj.ortho(*projection) modelview = QMatrix4x4() modelview.lookAt(self.camera_pos, self.camera_pos + self.camera_rot, QVector3D(0, 1, 0)) self.shaders.setUniformValue("ModelViewMatrix", modelview) self.shaders.setUniformValue("MVP", proj * modelview) def updateParams(self): """Update light, alpha and scaling parameters""" self.shaders.setUniformValue("LightPos", self.light_pos) self.shaders.setUniformValue("ambientStrength", self.ambient) self.shaders.setUniformValue("diffuseStrength", self.diffuse) self.shaders.setUniformValue("alpha", self.alpha) self.shaders.setUniformValue("center", self.center) self.shaders.setUniformValue("scale", self.scale_vec) def drawScene(self): """Paint all the prepared data""" GL.glClear(GL.GL_COLOR_BUFFER_BIT) self.shaders.setUniformValue('scaleEnabled', False) self.shaders.setUniformValue('phongModel', False) self.drawPreparedPolygons(*self.light_data) if self.show_light_lines: self.drawPreparedLines(*self.light_lines_data) self.shaders.setUniformValue('scaleEnabled', True) if self.show_grid: self.drawPreparedLines(*self.grid_data) # self.drawPreparedLines(*self.normal_data) self.shaders.setUniformValue('phongModel', True) self.drawPreparedPolygons(*self.map_data) if self.show_contour: self.shaders.setUniformValue('phongModel', False) self.drawPreparedLineStrips(self.contour_data) def drawPreparedPolygons(self, start, end): for i in range(start, end, 4): GL.glDrawArrays(GL.GL_POLYGON, i, 4) def drawPreparedLines(self, start, end): GL.glDrawArrays(GL.GL_LINES, start, end - start) def drawPreparedLineStrips(self, arr): for start, end in arr: GL.glDrawArrays(GL.GL_LINE_STRIP, start, end - start) # ==================== CONTROLS ==================== def setupControls(self): """Connect controls to this class' methods""" # Camera self.moveCameraUp.clicked.connect(lambda: self.moveCamera(az=1)) self.moveCameraDown.clicked.connect(lambda: self.moveCamera(az=-1)) self.moveCameraLeft.clicked.connect(lambda: self.moveCamera(pol=1)) self.moveCameraRight.clicked.connect(lambda: self.moveCamera(pol=-1)) self.moveCameraForward.clicked.connect(lambda: self.moveCamera(z=-1)) self.moveCameraBackward.clicked.connect(lambda: self.moveCamera(z=1)) # Scaling self.xScaleSpinBox.valueChanged.connect(lambda x: self.scaleView(x=x)) self.yScaleSpinBox.valueChanged.connect(lambda y: self.scaleView(y=y)) self.zScaleSpinBox.valueChanged.connect(lambda z: self.scaleView(z=z)) self.realPropCheckBox.stateChanged.connect( lambda: self.scaleView(y=self.yScaleSpinBox.value())) # Light self.ambientSlider.valueChanged.connect( lambda ambient: self.setLight(ambient=ambient / 100)) self.diffuseSlider.valueChanged.connect( lambda diffuse: self.setLight(diffuse=diffuse / 100)) self.xLightSpinBox.valueChanged.connect(lambda x: self.setLight(x=x)) self.yLightSpinBox.valueChanged.connect(lambda y: self.setLight(y=y)) self.zLightSpinBox.valueChanged.connect(lambda z: self.setLight(z=z)) # Display self.gridCheckBox.toggled.connect(lambda g: self.setGrid(show=g)) self.contourCheckBox.toggled.connect(lambda c: self.setContour(show=c)) self.contourLevelSpinBox.valueChanged.connect( lambda l: self.setContour(levels=l)) # Misc self.alphaSlider.valueChanged.connect( lambda alpha: self.setDisplay(alpha / 100)) self.actionGrabKeyboard.toggled.connect(self.toggleGrabKeyboard) self.actionGrabMouse.toggled.connect(self.toggleGrabMouse) self.orhogonalRadioButton.clicked.connect(self.openGLWidget.update) self.perspectiveRadioButton.clicked.connect(self.openGLWidget.update) self.openGLWidget.mousePressEvent \ = lambda event: self.actionGrabMouse.setChecked(True) def keyPressed(self, event): key = event.key() camera_dict = { Qt.Key_W: { 'z': 1 }, Qt.Key_S: { 'z': -1 }, Qt.Key_A: { 'x': -1 }, Qt.Key_D: { 'x': 1 }, Qt.Key_Z: { 'y': 1 }, Qt.Key_X: { 'y': -1 }, Qt.Key_Up: { 'az': 1 }, Qt.Key_Down: { 'az': -1 }, Qt.Key_Left: { 'pol': 1 }, Qt.Key_Right: { 'pol': -1 } } self.moveCamera(**camera_dict.get(key, {})) if key == Qt.Key_Escape: self.actionGrabMouse.setChecked(False) self.actionGrabKeyboard.setChecked(False) def mouseMoved(self, event): az_sensivity = 0.03 pol_sensivity = 0.03 if self.mouse_grabbed: delta = event.globalPos() - self.mouse_center QCursor.setPos(self.mouse_center) if event.buttons() == Qt.RightButton: self.moveCameraAroundCenter(az=delta.y() * az_sensivity, pol=delta.x() * pol_sensivity) else: self.moveCamera(az=delta.y() * az_sensivity, pol=delta.x() * pol_sensivity) else: super().mouseMoveEvent(event) def getRotVec(self): rot_matr = QMatrix4x4() rot_matr.rotate(-90, 0, 1, 0) rot_vec = QVector3D(self.camera_rot) rot_vec.setY(0) rot_vec = rot_matr * rot_vec return rot_vec @updateGL @updateCameraInfo def moveCamera(self, az=0, pol=0, x=0, y=0, z=0): move_coef = 0.1 rot_coef = 2 rot_vec = self.getRotVec() if az != 0 or pol != 0: rot_matr = QMatrix4x4() rot_matr.rotate(rot_coef * az, rot_vec) rot_matr.rotate(rot_coef * pol, 0, 1, 0) self.camera_rot = rot_matr * self.camera_rot if z: self.camera_pos += move_coef * self.camera_rot * z if x: self.camera_pos += rot_vec * move_coef * x if y: self.camera_pos.setY(self.camera_pos.y() + y * move_coef) @updateGL @updateCameraInfo def moveCameraAroundCenter(self, az=0, pol=0): rot_coef = 4 rot_vec = self.getRotVec() rot_matr = QMatrix4x4() rot_matr.rotate(rot_coef * az, rot_vec) rot_matr.rotate(rot_coef * pol, 0, 1, 0) rot_center = self.rot_center * self.scale_vec self.camera_pos -= rot_center self.camera_pos = rot_matr * self.camera_pos self.camera_pos += rot_center self.camera_rot = -self.camera_pos + rot_center @updateGL @updateCameraInfo def scaleView(self, x=None, y=None, z=None): if x: self.scale_vec.setX(x) if y: if self.realPropCheckBox.isChecked(): self.scale_vec.setY(y * self.real_prop) else: self.scale_vec.setY(y) if z: self.scale_vec.setZ(z) @updateGL def setLight(self, x=None, y=None, z=None, ambient=None, diffuse=None): if x: self.light_pos.setX(x) if y: self.light_pos.setY(y) if z: self.light_pos.setZ(z) if ambient: self.ambient = ambient if diffuse: self.diffuse = diffuse self.update_light = True @updateGL def setDisplay(self, alpha=None, invisible=None): if invisible: self.invisible = invisible if alpha: self.alpha = alpha @updateGL def setGrid(self, show): self.show_grid = show self.update_buffer = True @updateGL def setContour(self, show=None, levels=None): if show is not None: self.show_contour = show self.update_buffer = True if levels is not None: self.contour_levels = levels if self.show_contour: self.update_buffer = True def toggleGrabKeyboard(self, grab: bool): if grab: self.grabKeyboard() else: self.releaseKeyboard() def toggleGrabMouse(self, grab: bool): self.actionGrabKeyboard.setChecked(grab) self.mouse_grabbed = grab if grab: self.setCursor(Qt.BlankCursor) self.mouse_center = self.getMouseCenter() QCursor.setPos(self.mouse_center) self.setMouseTracking(True) self.grabMouse() else: self.setCursor(Qt.ArrowCursor) self.setMouseTracking(False) self.releaseMouse() def getMouseCenter(self): w, h = self.openGLWidget.width(), self.openGLWidget.height() local_center = QPoint(w / 2, h / 2) global_center = self.mapToGlobal(self.openGLWidget.pos()) \ + local_center return global_center
class SquircleRenderer(QObject): # QOpenGLFunctions """docstring for SquircleRenderer""" def __init__(self, parent=None): super(SquircleRenderer, self).__init__(parent) self.m_t = 0.0 self.m_program = None self.m_viewportSize = QSize() def setT(self, t): self.m_t = t def setViewportSize(self, size): self.m_viewportSize = size def setWin(self, win): self.win = win ver = QOpenGLVersionProfile() ver.setVersion(2, 1) self.m_context = self.win.openglContext() self.gl = self.m_context.versionFunctions(ver) @pyqtSlot() def paint(self): if not self.m_program: self.gl.initializeOpenGLFunctions() self.m_program = QOpenGLShaderProgram(self) self.m_program.addShaderFromSourceCode( QOpenGLShader.Vertex, "attribute highp vec4 vertices;" "varying highp vec2 coords;" "void main() {" " gl_Position = vertices;" " coords = vertices.xy;" "}", ) self.m_program.addShaderFromSourceCode( QOpenGLShader.Fragment, "uniform lowp float t;" "varying highp vec2 coords;" "void main() {" " lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));" " i = smoothstep(t - 0.8, t + 0.8, i);" " i = floor(i * 20.) / 20.;" " gl_FragColor = vec4(coords * .5 + .5, i, i);" "}", ) self.m_program.bindAttributeLocation("vertices", 0) self.m_program.link() self.m_program.bind() self.m_program.enableAttributeArray(0) values = [(-1, -1), (1, -1), (-1, 1), (1, 1)] self.m_program.setAttributeArray(0, values) self.m_program.setUniformValue("t", self.m_t) # print("DATA:",self.m_viewportSize.width(), self.m_viewportSize.height(), self.m_t)#, self.gl.glViewport) self.gl.glViewport(0, 0, self.m_viewportSize.width(), self.m_viewportSize.height()) self.gl.glDisable(self.gl.GL_DEPTH_TEST) self.gl.glClearColor(0, 0, 0, 1) self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT) self.gl.glEnable(self.gl.GL_BLEND) self.gl.glBlendFunc(self.gl.GL_SRC_ALPHA, self.gl.GL_ONE) self.gl.glDrawArrays(self.gl.GL_TRIANGLE_STRIP, 0, 4) self.m_program.disableAttributeArray(0) self.m_program.release()
class LogoRenderer():#protected QOpenGLFunctions """docstring for LogoRenderer""" def __init__(self): super(LogoRenderer, self).__init__() self.m_fAngle = None self.m_fScale = None self.vertices = [] self.normals = [] self.program1 = QOpenGLShaderProgram() self.vertexAttr1 = 0 self.normalAttr1 = 0 self.matrixUniform1 = 0 ver = QOpenGLVersionProfile() ver.setVersion(2, 1) cntx = QOpenGLContext.currentContext() #print("QOpenGLContext:", cntx, ver) fmt = cntx.format() fmt.setVersion(2, 1) cntx.setFormat(fmt) self.gl = cntx.versionFunctions(ver) def render(self): self.gl.glDepthMask(True) self.gl.glClearColor(0.5, 0.5, 0.7, 1.0) self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT) self.gl.glTexParameteri(self.gl.GL_TEXTURE_2D, self.gl.GL_TEXTURE_MIN_FILTER, self.gl.GL_LINEAR ) self.gl.glTexParameteri(self.gl.GL_TEXTURE_2D, self.gl.GL_TEXTURE_MAG_FILTER, self.gl.GL_LINEAR ) self.gl.glFrontFace(self.gl.GL_CW) self.gl.glCullFace(self.gl.GL_FRONT) self.gl.glEnable(self.gl.GL_CULL_FACE) self.gl.glEnable(self.gl.GL_DEPTH_TEST) modelview = QMatrix4x4() modelview.rotate(self.m_fAngle, 0.0, 1.0, 0.0) modelview.rotate(self.m_fAngle, 1.0, 0.0, 0.0) modelview.rotate(self.m_fAngle, 0.0, 0.0, 1.0) modelview.scale(self.m_fScale) modelview.translate(0.0, -0.2, 0.0) self.program1.bind() self.program1.setUniformValue(self.matrixUniform1, modelview) self.paintQtLogo() self.program1.release() self.gl.glDisable(self.gl.GL_DEPTH_TEST) self.gl.glDisable(self.gl.GL_CULL_FACE) self.m_fAngle += 1.0 def initialize(self): #print("initialize.gls") self.gl.initializeOpenGLFunctions() self.gl.glClearColor(0.1, 0.1, 0.2, 1.0) vshader1 = QOpenGLShader(QOpenGLShader.Vertex, self.program1) vsrc1 = str("attribute highp vec4 vertex;\n" "attribute mediump vec3 normal;\n" "uniform mediump mat4 matrix;\n" "varying mediump vec4 color;\n" "void main(void)\n" "{\n" " vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n" " float angle = max(dot(normal, toLight), 0.0);\n" " vec3 col = vec3(0.40, 1.0, 0.0);\n" " color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n" " color = clamp(color, 0.0, 1.0);\n" " gl_Position = matrix * vertex;\n" "}\n") vshader1.compileSourceCode(vsrc1) fshader1 = QOpenGLShader(QOpenGLShader.Fragment, self.program1) fsrc1 = str("varying mediump vec4 color;\n" "void main(void)\n" "{\n" " gl_FragColor = color;\n" "}\n") fshader1.compileSourceCode(fsrc1) self.program1.addShader(vshader1) self.program1.addShader(fshader1) self.program1.link() self.vertexAttr1 = self.program1.attributeLocation("vertex") self.normalAttr1 = self.program1.attributeLocation("normal") self.matrixUniform1 = self.program1.uniformLocation("matrix") self.gl.glTexParameteri(self.gl.GL_TEXTURE_2D, self.gl.GL_TEXTURE_MIN_FILTER, self.gl.GL_LINEAR ) self.gl.glTexParameteri(self.gl.GL_TEXTURE_2D, self.gl.GL_TEXTURE_MAG_FILTER, self.gl.GL_LINEAR ) self.m_fAngle = 0 self.m_fScale = 1 self.createGeometry() def paintQtLogo(self): self.program1.enableAttributeArray(self.normalAttr1) self.program1.enableAttributeArray(self.vertexAttr1) self.program1.setAttributeArray(self.vertexAttr1, self.vertices) self.program1.setAttributeArray(self.normalAttr1, self.normals) self.gl.glDrawArrays(self.gl.GL_TRIANGLES, 0, len(self.vertices)) self.program1.disableAttributeArray(self.normalAttr1) self.program1.disableAttributeArray(self.vertexAttr1) def createGeometry(self): self.vertices.clear() self.normals.clear() x1 = +0.06 y1 = -0.14 x2 = +0.14 y2 = -0.06 x3 = +0.08 y3 = +0.00 x4 = +0.30 y4 = +0.22 self.quad(x1, y1, x2, y2, y2, x2, y1, x1) self.quad(x3, y3, x4, y4, y4, x4, y3, x3) self.extrude(x1, y1, x2, y2) self.extrude(x2, y2, y2, x2) self.extrude(y2, x2, y1, x1) self.extrude(y1, x1, x1, y1) self.extrude(x3, y3, x4, y4) self.extrude(x4, y4, y4, x4) self.extrude(y4, x4, y3, x3) NumSectors = 100 for i in range(NumSectors): angle1 = (i * 2 * math.pi) / NumSectors x5 = 0.30 * math.sin(angle1) y5 = 0.30 * math.cos(angle1) x6 = 0.20 * math.sin(angle1) y6 = 0.20 * math.cos(angle1) angle2 = ((i + 1) * 2 * math.pi) / NumSectors x7 = 0.20 * math.sin(angle2) y7 = 0.20 * math.cos(angle2) x8 = 0.30 * math.sin(angle2) y8 = 0.30 * math.cos(angle2) self.quad(x5, y5, x6, y6, x7, y7, x8, y8) self.extrude(x6, y6, x7, y7) self.extrude(x8, y8, x5, y5) for i in range(len(self.vertices)): self.vertices[i] *= 2.0 def quad(self, x1, y1, x2, y2, x3, y3, x4, y4): #print("quad inicio") self.vertices.append(QVector3D(x1, y1, -0.05)) self.vertices.append(QVector3D(x2, y2, -0.05)) self.vertices.append(QVector3D(x4, y4, -0.05)) self.vertices.append(QVector3D(x3, y3, -0.05)) self.vertices.append(QVector3D(x4, y4, -0.05)) self.vertices.append(QVector3D(x2, y2, -0.05)) n = QVector3D.normal(QVector3D(x2 - x1, y2 - y1, 0.0), QVector3D(x4 - x1, y4 - y1, 0.0)) for i in range(6): self.normals.append(n) self.vertices.append(QVector3D(x4, y4, 0.05)) self.vertices.append(QVector3D(x2, y2, 0.05)) self.vertices.append(QVector3D(x1, y1, 0.05)) self.vertices.append(QVector3D(x2, y2, 0.05)) self.vertices.append(QVector3D(x4, y4, 0.05)) self.vertices.append(QVector3D(x3, y3, 0.05)) n = QVector3D.normal(QVector3D(x2 - x4, y2 - y4, 0.0), QVector3D(x1 - x4, y1 - y4, 0.0)) for i in range(6): self.normals.append(n) #print("quad fin") def extrude(self, x1, y1, x2, y2): #print("extrude inicio") self.vertices.append(QVector3D(x1, y1, +0.05)) self.vertices.append(QVector3D(x2, y2, +0.05)) self.vertices.append(QVector3D(x1, y1, -0.05)) self.vertices.append(QVector3D(x2, y2, -0.05)) self.vertices.append(QVector3D(x1, y1, -0.05)) self.vertices.append(QVector3D(x2, y2, +0.05)) n = QVector3D.normal(QVector3D(x2 - x1, y2 - y1, 0.0), QVector3D(0.0, 0.0, -0.1)) for i in range(6): self.normals.append(n)