123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- # -*- coding: utf-8 -*-
- #===============================================================================
- #
- # PyGLWidget.py
- #
- # A simple GL Viewer.
- #
- # Copyright (c) 2011, Arne Schmitz <arne.schmitz@gmx.net>
- # 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 <organization> 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 <COPYRIGHT HOLDER> 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:
- #
- #===============================================================================
|