diff --git a/examples/GLVolumeItem.py b/examples/GLVolumeItem.py index 163f4b7a..3f5b5fb2 100644 --- a/examples/GLVolumeItem.py +++ b/examples/GLVolumeItem.py @@ -46,11 +46,17 @@ d2[..., 1] = negative * (255./negative.max()) d2[..., 2] = d2[...,1] d2[..., 3] = d2[..., 0]*0.3 + d2[..., 1]*0.3 d2[..., 3] = (d2[..., 3].astype(float) / 255.) **2 * 255 - + +d2[:, 0, 0] = [255,0,0,100] +d2[0, :, 0] = [0,255,0,100] +d2[0, 0, :] = [0,0,255,100] + v = gl.GLVolumeItem(d2) v.translate(-50,-50,-100) w.addItem(v) +ax = gl.GLAxisItem() +w.addItem(ax) ## Start Qt event loop unless running in interactive mode. if sys.flags.interactive != 1: diff --git a/functions.py b/functions.py index 345b39d7..9b41da4c 100644 --- a/functions.py +++ b/functions.py @@ -316,6 +316,13 @@ def intColor(index, hues=9, values=1, maxValue=255, minValue=150, maxHue=360, mi c.setAlpha(alpha) return c +def glColor(*args, **kargs): + """ + Convert a color to OpenGL color format (r,g,b,a) floats 0.0-1.0 + Accepts same arguments as :func:`mkColor `. + """ + c = mkColor(*args, **kargs) + return (c.red()/255., c.green()/255., c.blue()/255., c.alpha()/255.) def affineSlice(data, shape, origin, vectors, axes, **kargs): """ diff --git a/opengl/GLGraphicsItem.py b/opengl/GLGraphicsItem.py index da6a37c6..96cc6763 100644 --- a/opengl/GLGraphicsItem.py +++ b/opengl/GLGraphicsItem.py @@ -53,6 +53,10 @@ class GLGraphicsItem(QtCore.QObject): self.__transform = tr self.update() + def resetTransform(self): + self.__transform.setToIdentity() + self.update() + def applyTransform(self, tr, local): """ Multiply this object's transform by *tr*. diff --git a/opengl/GLViewWidget.py b/opengl/GLViewWidget.py index 15077b74..6aeeabfe 100644 --- a/opengl/GLViewWidget.py +++ b/opengl/GLViewWidget.py @@ -14,6 +14,8 @@ class GLViewWidget(QtOpenGL.QGLWidget): """ def __init__(self, parent=None): QtOpenGL.QGLWidget.__init__(self, parent) + self.setFocusPolicy(QtCore.Qt.ClickFocus) + self.opts = { 'center': Vector(0,0,0), ## will always appear at the center of the widget 'distance': 10.0, ## distance of camera from center @@ -23,6 +25,10 @@ class GLViewWidget(QtOpenGL.QGLWidget): ## (rotation around z-axis 0 points along x-axis) } self.items = [] + self.noRepeatKeys = [QtCore.Qt.Key_Right, QtCore.Qt.Key_Left, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down, QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown] + self.keysPressed = {} + self.keyTimer = QtCore.QTimer() + self.keyTimer.timeout.connect(self.evalKeyState) def addItem(self, item): self.items.append(item) @@ -31,12 +37,12 @@ class GLViewWidget(QtOpenGL.QGLWidget): item.initializeGL() item._setView(self) #print "set view", item, self, item.view() - self.updateGL() + self.update() def removeItem(self, item): self.items.remove(item) item._setView(None) - self.updateGL() + self.update() def initializeGL(self): @@ -45,7 +51,7 @@ class GLViewWidget(QtOpenGL.QGLWidget): def resizeGL(self, w, h): glViewport(0, 0, w, h) - #self.updateGL() + #self.update() def setProjection(self): ## Create the projection matrix @@ -59,7 +65,7 @@ class GLViewWidget(QtOpenGL.QGLWidget): nearClip = dist * 0.001 farClip = dist * 1000. - r = nearClip * np.tan(fov) + r = nearClip * np.tan(fov * 0.5 * np.pi / 180.) t = r * h / w glFrustum( -r, r, -t, t, nearClip, farClip) @@ -70,7 +76,7 @@ class GLViewWidget(QtOpenGL.QGLWidget): glRotatef(self.opts['elevation']-90, 1, 0, 0) glRotatef(self.opts['azimuth']+90, 0, 0, -1) center = self.opts['center'] - glTranslatef(center.x(), center.y(), center.z()) + glTranslatef(-center.x(), -center.y(), -center.z()) def paintGL(self): @@ -90,7 +96,15 @@ class GLViewWidget(QtOpenGL.QGLWidget): if not i.visible(): continue if i is item: - i.paint() + try: + glPushAttrib(GL_ALL_ATTRIB_BITS) + i.paint() + except: + import sys + sys.excepthook(*sys.exc_info()) + print "Error while drawing item", i + finally: + glPopAttrib(GL_ALL_ATTRIB_BITS) else: glMatrixMode(GL_MODELVIEW) glPushMatrix() @@ -117,23 +131,110 @@ class GLViewWidget(QtOpenGL.QGLWidget): return pos + def orbit(self, azim, elev): + """Orbits the camera around the center position. *azim* and *elev* are given in degrees.""" + self.opts['azimuth'] += azim + self.opts['elevation'] = np.clip(self.opts['elevation'] + elev, -90, 90) + self.update() + + def pan(self, dx, dy, dz, relative=False): + """ + Moves the center (look-at) position while holding the camera in place. + + If relative=True, then the coordinates are interpreted such that x + if in the global xy plane and points to the right side of the view, y is + in the global xy plane and orthogonal to x, and z points in the global z + direction. Distances are scaled roughly such that a value of 1.0 moves + by one pixel on screen. + + """ + if not relative: + self.opts['center'] += QtGui.QVector3D(dx, dy, dz) + else: + cPos = self.cameraPosition() + cVec = self.opts['center'] - cPos + dist = cVec.length() ## distance from camera to center + xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) ## approx. width of view at distance of center point + xScale = xDist / self.width() + zVec = QtGui.QVector3D(0,0,1) + xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized() + yVec = QtGui.QVector3D.crossProduct(xVec, zVec).normalized() + self.opts['center'] = self.opts['center'] + xVec * xScale * dx + yVec * xScale * dy + zVec * xScale * dz + self.update() + + def pixelSize(self, pos): + """ + Return the approximate size of a screen pixel at the location pos + + """ + cam = self.cameraPosition() + dist = (pos-cam).length() + xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) + return xDist / self.width() + def mousePressEvent(self, ev): self.mousePos = ev.pos() def mouseMoveEvent(self, ev): diff = ev.pos() - self.mousePos self.mousePos = ev.pos() - self.opts['azimuth'] -= diff.x() - self.opts['elevation'] = np.clip(self.opts['elevation'] + diff.y(), -90, 90) - #print self.opts['azimuth'], self.opts['elevation'] - self.updateGL() + + if ev.buttons() == QtCore.Qt.LeftButton: + self.orbit(-diff.x(), diff.y()) + #print self.opts['azimuth'], self.opts['elevation'] + elif ev.buttons() == QtCore.Qt.MidButton: + if (ev.modifiers() & QtCore.Qt.ControlModifier): + self.pan(diff.x(), 0, diff.y(), relative=True) + else: + self.pan(diff.x(), diff.y(), 0, relative=True) def mouseReleaseEvent(self, ev): pass def wheelEvent(self, ev): - self.opts['distance'] *= 0.999**ev.delta() - self.updateGL() - + if (ev.modifiers() & QtCore.Qt.ControlModifier): + self.opts['fov'] *= 0.999**ev.delta() + else: + self.opts['distance'] *= 0.999**ev.delta() + self.update() + def keyPressEvent(self, ev): + if ev.key() in self.noRepeatKeys: + ev.accept() + if ev.isAutoRepeat(): + return + self.keysPressed[ev.key()] = 1 + self.evalKeyState() + + def keyReleaseEvent(self, ev): + if ev.key() in self.noRepeatKeys: + ev.accept() + if ev.isAutoRepeat(): + return + try: + del self.keysPressed[ev.key()] + except: + self.keysPressed = {} + self.evalKeyState() + + def evalKeyState(self): + speed = 2.0 + if len(self.keysPressed) > 0: + for key in self.keysPressed: + if key == QtCore.Qt.Key_Right: + self.orbit(azim=-speed, elev=0) + elif key == QtCore.Qt.Key_Left: + self.orbit(azim=speed, elev=0) + elif key == QtCore.Qt.Key_Up: + self.orbit(azim=0, elev=-speed) + elif key == QtCore.Qt.Key_Down: + self.orbit(azim=0, elev=speed) + elif key == QtCore.Qt.Key_PageUp: + pass + elif key == QtCore.Qt.Key_PageDown: + pass + self.keyTimer.start(16) + else: + self.keyTimer.stop() + \ No newline at end of file diff --git a/opengl/items/GLVolumeItem.py b/opengl/items/GLVolumeItem.py index 01d8ae48..0a3a421b 100644 --- a/opengl/items/GLVolumeItem.py +++ b/opengl/items/GLVolumeItem.py @@ -71,6 +71,7 @@ class GLVolumeItem(GLGraphicsItem): view = self.view() center = QtGui.QVector3D(*[x/2. for x in self.data.shape[:3]]) cam = self.mapFromParent(view.cameraPosition()) - center + #print "center", center, "cam", view.cameraPosition(), self.mapFromParent(view.cameraPosition()), "diff", cam cam = np.array([cam.x(), cam.y(), cam.z()]) ax = np.argmax(abs(cam)) d = 1 if cam[ax] > 0 else -1