# -*- coding: utf-8 -*- #=============================================================================== # # PyGLWidget.py # # A simple GL Viewer. # # Copyright (c) 2011, Arne Schmitz # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # #=============================================================================== from PyQt5 import QtCore, QtGui, QtOpenGL import math import numpy import numpy.linalg as linalg import OpenGL OpenGL.ERROR_CHECKING = True from OpenGL.GL import * from OpenGL.GLU import * class PyGLWidget(QtOpenGL.QGLWidget): # Qt signals signalGLMatrixChanged = QtCore.pyqtSignal() rotationBeginEvent = QtCore.pyqtSignal() rotationEndEvent = QtCore.pyqtSignal() def __init__(self, parent = None): format = QtOpenGL.QGLFormat() format.setSampleBuffers(True) QtOpenGL.QGLWidget.__init__(self, format, parent) self.setCursor(QtCore.Qt.OpenHandCursor) self.setMouseTracking(True) self.modelview_matrix_ = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [-10.53889084,-2.01535511,-25.95999146,1. ]] self.translate_vector_ = [0, 0, 0.0] self.viewport_matrix_ = [] self.projection_matrix_ = [] self.near_ = 0.1 self.far_ = 100.0 self.fovy_ = 45.0 self.radius_ = 2.0 self.last_point_2D_ = QtCore.QPoint() self.last_point_ok_ = False self.last_point_3D_ = [1.0, 0.0, 0.0] self.isInRotation_ = False # connections #self.signalGLMatrixChanged.connect(self.printModelViewMatrix) @QtCore.pyqtSlot() def printModelViewMatrix(self): print (self.modelview_matrix_) def initializeGL(self): # OpenGL state glClearColor(0.6, 0.6, 0.6, 0.5) glEnable(GL_DEPTH_TEST) self.reset_view() self.translate([0,11.5,-35.]) def resizeGL(self, width, height): glViewport(0, 0, width, height ); self.set_projection( self.near_, self.far_, self.fovy_ ); self.updateGL() def paintGL(self): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glMatrixMode(GL_MODELVIEW) glLoadMatrixd(self.modelview_matrix_) #self.printModelViewMatrix() def set_projection(self, _near, _far, _fovy): self.near_ = _near self.far_ = _far self.fovy_ = _fovy self.makeCurrent() glMatrixMode( GL_PROJECTION ) glLoadIdentity() gluPerspective( self.fovy_, float(self.width()) / float(self.height()), self.near_, self.far_ ) self.updateGL() def set_center(self, _cog): self.center_ = _cog self.view_all() def set_radius(self, _radius): self.radius_ = _radius self.set_projection(_radius / 100.0, _radius * 100.0, self.fovy_) self.reset_view() self.translate([0, 0, -_radius * 2.0]) self.view_all() self.updateGL() def reset_view(self): # scene pos and size glMatrixMode( GL_MODELVIEW ) glLoadIdentity(); self.modelview_matrix_ = glGetDoublev( GL_MODELVIEW_MATRIX ) self.set_center([0.0, 0.0, 0.0]) def reset_rotation(self): self.modelview_matrix_[0] = [1.0, 0.0, 0.0, 0.0] self.modelview_matrix_[1] = [0.0, 1.0, 0.0, 0.0] self.modelview_matrix_[2] = [0.0, 0.0, 1.0, 0.0] glMatrixMode(GL_MODELVIEW) glLoadMatrixd(self.modelview_matrix_) self.updateGL() def translate(self, _trans): # Translate the object by _trans # Update modelview_matrix_ self.makeCurrent() glMatrixMode(GL_MODELVIEW) glLoadIdentity() glTranslated(_trans[0], _trans[1], _trans[2]) glMultMatrixd(self.modelview_matrix_) self.modelview_matrix_ = glGetDoublev(GL_MODELVIEW_MATRIX) self.translate_vector_[0] = self.modelview_matrix_[3][0] self.translate_vector_[1] = self.modelview_matrix_[3][1] self.translate_vector_[2] = self.modelview_matrix_[3][2] self.signalGLMatrixChanged.emit() def rotate(self, _axis, _angle): t = [self.modelview_matrix_[0][0] * self.center_[0] + self.modelview_matrix_[1][0] * self.center_[1] + self.modelview_matrix_[2][0] * self.center_[2] + self.modelview_matrix_[3][0], self.modelview_matrix_[0][1] * self.center_[0] + self.modelview_matrix_[1][1] * self.center_[1] + self.modelview_matrix_[2][1] * self.center_[2] + self.modelview_matrix_[3][1], self.modelview_matrix_[0][2] * self.center_[0] + self.modelview_matrix_[1][2] * self.center_[1] + self.modelview_matrix_[2][2] * self.center_[2] + self.modelview_matrix_[3][2]] self.makeCurrent() glLoadIdentity() glTranslatef(t[0], t[1], t[2]) glRotated(_angle, _axis[0], _axis[1], _axis[2]) glTranslatef(-t[0], -t[1], -t[2]) glMultMatrixd(self.modelview_matrix_) self.modelview_matrix_ = glGetDoublev(GL_MODELVIEW_MATRIX) self.signalGLMatrixChanged.emit() def view_all(self): self.translate( [ -( self.modelview_matrix_[0][0] * self.center_[0] + self.modelview_matrix_[0][1] * self.center_[1] + self.modelview_matrix_[0][2] * self.center_[2] + self.modelview_matrix_[0][3]), -( self.modelview_matrix_[1][0] * self.center_[0] + self.modelview_matrix_[1][1] * self.center_[1] + self.modelview_matrix_[1][2] * self.center_[2] + self.modelview_matrix_[1][3]), -( self.modelview_matrix_[2][0] * self.center_[0] + self.modelview_matrix_[2][1] * self.center_[1] + self.modelview_matrix_[2][2] * self.center_[2] + self.modelview_matrix_[2][3] + self.radius_ / 2.0 )]) def map_to_sphere(self, _v2D): _v3D = [0.0, 0.0, 0.0] # inside Widget? if (( _v2D.x() >= 0 ) and ( _v2D.x() <= self.width() ) and ( _v2D.y() >= 0 ) and ( _v2D.y() <= self.height() ) ): # map Qt Coordinates to the centered unit square [-0.5..0.5]x[-0.5..0.5] x = float( _v2D.x() - 0.5 * self.width()) / self.width() y = float( 0.5 * self.height() - _v2D.y()) / self.height() _v3D[0] = x; _v3D[1] = y; # use Pythagoras to comp z-coord (the sphere has radius sqrt(2.0*0.5*0.5)) z2 = 2.0*0.5*0.5-x*x-y*y; # numerical robust sqrt _v3D[2] = math.sqrt(max( z2, 0.0 )) # normalize direction to unit sphere n = linalg.norm(_v3D) _v3D = numpy.array(_v3D) / n return True, _v3D else: return False, _v3D def wheelEvent(self, _event): # Use the mouse wheel to zoom in/out d = (_event.angleDelta().y()) / 250.0 * self.radius_ self.translate([0.0, 0.0, d]) self.updateGL() _event.accept() def mousePressEvent(self, _event): self.last_point_2D_ = _event.pos() self.last_point_ok_, self.last_point_3D_ = self.map_to_sphere(self.last_point_2D_) def mouseMoveEvent(self, _event): newPoint2D = _event.pos() if ((newPoint2D.x() < 0) or (newPoint2D.x() > self.width()) or (newPoint2D.y() < 0) or (newPoint2D.y() > self.height())): return # Left button: rotate around center_ # Middle button: translate object # Left & middle button: zoom in/out value_y = 0 newPoint_hitSphere, newPoint3D = self.map_to_sphere(newPoint2D) dx = float(newPoint2D.x() - self.last_point_2D_.x()) dy = float(newPoint2D.y() - self.last_point_2D_.y()) w = float(self.width()) h = float(self.height()) # enable GL context self.makeCurrent() # move in z direction if (((_event.buttons() & QtCore.Qt.LeftButton) and (_event.buttons() & QtCore.Qt.MidButton)) or (_event.buttons() & QtCore.Qt.LeftButton and _event.modifiers() & QtCore.Qt.ControlModifier)): value_y = self.radius_ * dy * 2.0 / h; self.translate([0.0, 0.0, value_y]) #rotate elif (_event.buttons() & QtCore.Qt.MidButton or (_event.buttons() & QtCore.Qt.LeftButton and _event.modifiers() & QtCore.Qt.ShiftModifier)): #中间键 旋转 '''if (not self.isInRotation_): self.isInRotation_ = True self.rotationBeginEvent.emit() axis = [0.0, 0.0, 0.0] angle = 0.0 if (self.last_point_ok_ and newPoint_hitSphere): axis = numpy.cross(self.last_point_3D_, newPoint3D) cos_angle = numpy.dot(self.last_point_3D_, newPoint3D) if (abs(cos_angle) < 1.0): angle = math.acos(cos_angle) * 180.0 / math.pi angle *= 2.0 self.rotate(axis, angle)''' # move in x,y direction elif (_event.buttons() & QtCore.Qt.LeftButton): #左键移动 z = - (self.modelview_matrix_[0][2] * self.center_[0] + self.modelview_matrix_[1][2] * self.center_[1] + self.modelview_matrix_[2][2] * self.center_[2] + self.modelview_matrix_[3][2]) / (self.modelview_matrix_[0][3] * self.center_[0] + self.modelview_matrix_[1][3] * self.center_[1] + self.modelview_matrix_[2][3] * self.center_[2] + self.modelview_matrix_[3][3]) fovy = 45.0 aspect = w / h n = 0.01 * self.radius_ up = math.tan(fovy / 2.0 * math.pi / 180.0) * n right = aspect * up self.translate( [2.0 * dx / w * right / n * z, -2.0 * dy / h * up / n * z, 0.0] ) # remember this point self.last_point_2D_ = newPoint2D self.last_point_3D_ = newPoint3D self.last_point_ok_ = newPoint_hitSphere # trigger redraw self.updateGL() def mouseReleaseEvent(self, _event): if (self.isInRotation_): self.isInRotation_ = False self.rotationEndEvent.emit() last_point_ok_ = False #=============================================================================== # # Local Variables: # mode: Python # indent-tabs-mode: nil # End: # #===============================================================================