home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/env python
-
- # This is statement is required by the build system to query build info
- if __name__ == '__build__':
- raise Exception
-
- # A class that creates an opengl widget.
- # Mike Hartshorn
- # Department of Chemistry
- # University of York, UK
- #
-
- from OpenGL.GL import *
- from OpenGL.GLU import *
- from Tkinter import _default_root
- from Tkinter import *
- import math
- import os,sys
-
- #
-
- def glTranslateScene(s, x, y, mousex, mousey):
- glMatrixMode(GL_MODELVIEW)
- mat = glGetDoublev(GL_MODELVIEW_MATRIX)
- glLoadIdentity()
- glTranslatef(s * (x - mousex), s * (mousey - y), 0.0)
- glMultMatrixd(mat)
-
-
- def glRotateScene(s, xcenter, ycenter, zcenter, x, y, mousex, mousey):
- glMatrixMode(GL_MODELVIEW)
- mat = glGetDoublev(GL_MODELVIEW_MATRIX)
- glLoadIdentity()
- glTranslatef(xcenter, ycenter, zcenter)
- glRotatef(s * (y - mousey), 1., 0., 0.)
- glRotatef(s * (x - mousex), 0., 1., 0.)
- glTranslatef(-xcenter, -ycenter, -zcenter)
- glMultMatrixd(mat)
-
-
- def sub(x, y):
- return map(lambda a, b: a-b, x, y)
-
-
- def dot(x, y):
- t = 0
- for i in range(len(x)):
- t = t + x[i]*y[i]
- return t
-
-
- def glDistFromLine(x, p1, p2):
- f = map(lambda x, y: x-y, p2, p1)
- g = map(lambda x, y: x-y, x, p1)
- return dot(g, g) - dot(f, g)**2/dot(f, f)
-
-
- # Keith Junius <junius@chem.rug.nl> provided many changes to Togl
- TOGL_NORMAL = 1
- TOGL_OVERLAY = 2
-
- def v3distsq(a,b):
- d = ( a[0] - b[0], a[1] - b[1], a[2] - b[2] )
- return d[0]*d[0] + d[1]*d[1] + d[2]*d[2]
-
- # new version from Daniel Faken (Daniel_Faken@brown.edu) for static
- # loading comptability
- if _default_root is None:
- _default_root = Tk()
-
- # add this file's directory to Tcl's search path
- # on Linux Togl is installed in ./linux2-tk8.0
- # on Windows (Python2.0) in ./win32-tk8.3
- try:
- TOGL_DLL_PATH = os.path.join(
- os.path.dirname(__file__),
- sys.platform + "-tk" + _default_root.getvar("tk_version")
- )
- except NameError, err:
- # no __file__, likely running as an egg
- TOGL_DLL_PATH = ""
- _default_root.tk.call('lappend', 'auto_path', TOGL_DLL_PATH)
- try:
- _default_root.tk.eval('load {} Togl')
- except TclError:
- pass
- _default_root.tk.call('package', 'require', 'Togl')
-
- # This code is needed to avoid faults on sys.exit()
- # [DAA, Jan 1998]
- import sys
- oldexitfunc = None
- if hasattr(sys, 'exitfunc'):
- oldexitfunc = sys.exitfunc
- def cleanup():
- from Tkinter import _default_root, TclError
- import Tkinter
- try:
- if _default_root: _default_root.destroy()
- except TclError:
- pass
- Tkinter._default_root = None
- if oldexitfunc: oldexitfunc()
- sys.exitfunc = cleanup
- # [end DAA]
-
- class Togl(Widget):
- """
- Togl Widget
- Keith Junius
- Department of Biophysical Chemistry
- University of Groningen, The Netherlands
- Very basic widget which provides access to Togl functions.
- N.B. this requires a modified version of Togl 1.5 to gain access to the
- extra functionality. This support should be included in Togl 1.6, I hope.
- """
-
-
- def __init__(self, master=None, cnf={}, **kw):
- Widget.__init__(self, master, 'togl', cnf, kw)
-
-
- def render(self):
- return
-
-
- def swapbuffers(self):
- self.tk.call(self._w, 'swapbuffers')
-
-
- def makecurrent(self):
- self.tk.call(self._w, 'makecurrent')
-
-
- def alloccolor(self, red, green, blue):
- return self.tk.getint(self.tk.call(self._w, 'alloccolor', red, green, blue))
-
-
- def freecolor(self, index):
- self.tk.call(self._w, 'freecolor', index)
-
-
- def setcolor(self, index, red, green, blue):
- self.tk.call(self._w, 'setcolor', index, red, green, blue)
-
-
- def loadbitmapfont(self, fontname):
- return self.tk.getint(self.tk.call(self._w, 'loadbitmapfont', fontname))
-
-
- def unloadbitmapfont(self, fontbase):
- self.tk.call(self._w, 'unloadbitmapfont', fontbase)
-
-
- def uselayer(self, layer):
- self.tk.call(self._w, 'uselayer', layer)
-
-
- def showoverlay(self):
- self.tk.call(self._w, 'showoverlay')
-
-
- def hideoverlay(self):
- self.tk.call(self._w, 'hideoverlay')
-
-
- def existsoverlay(self):
- return self.tk.getboolean(self.tk.call(self._w, 'existsoverlay'))
-
-
- def getoverlaytransparentvalue(self):
- return self.tk.getint(self.tk.call(self._w, 'getoverlaytransparentvalue'))
-
-
- def ismappedoverlay(self):
- return self.tk.getboolean(self.tk.call(self._w, 'ismappedoverlay'))
-
-
- def alloccoloroverlay(self, red, green, blue):
- return self.tk.getint(self.tk.call(self._w, 'alloccoloroverlay', red, green, blue))
-
-
- def freecoloroverlay(self, index):
- self.tk.call(self._w, 'freecoloroverlay', index)
-
-
-
- class RawOpengl(Widget, Misc):
- """Widget without any sophisticated bindings\
- by Tom Schwaller"""
-
-
- def __init__(self, master=None, cnf={}, **kw):
- Widget.__init__(self, master, 'togl', cnf, kw)
- self.bind('<Map>', self.tkMap)
- self.bind('<Expose>', self.tkExpose)
- self.bind('<Configure>', self.tkExpose)
-
-
- def tkRedraw(self, *dummy):
- # This must be outside of a pushmatrix, since a resize event
- # will call redraw recursively.
- self.update_idletasks()
- self.tk.call(self._w, 'makecurrent')
- _mode = glGetDoublev(GL_MATRIX_MODE)
- try:
- glMatrixMode(GL_PROJECTION)
- glPushMatrix()
- try:
- self.redraw()
- glFlush()
- finally:
- glPopMatrix()
- finally:
- glMatrixMode(_mode)
- self.tk.call(self._w, 'swapbuffers')
-
-
- def tkMap(self, *dummy):
- self.tkExpose()
-
-
- def tkExpose(self, *dummy):
- self.tkRedraw()
-
-
-
-
- class Opengl(RawOpengl):
- """\
- Tkinter bindings for an Opengl widget.
- Mike Hartshorn
- Department of Chemistry
- University of York, UK
- http://www.yorvic.york.ac.uk/~mjh/
- """
-
- def __init__(self, master=None, cnf={}, **kw):
- """\
- Create an opengl widget.
- Arrange for redraws when the window is exposed or when
- it changes size."""
-
- #Widget.__init__(self, master, 'togl', cnf, kw)
- apply(RawOpengl.__init__, (self, master, cnf), kw)
- self.initialised = 0
-
- # Current coordinates of the mouse.
- self.xmouse = 0
- self.ymouse = 0
-
- # Where we are centering.
- self.xcenter = 0.0
- self.ycenter = 0.0
- self.zcenter = 0.0
-
- # The _back color
- self.r_back = 1.
- self.g_back = 0.
- self.b_back = 1.
-
- # Where the eye is
- self.distance = 10.0
-
- # Field of view in y direction
- self.fovy = 30.0
-
- # Position of clipping planes.
- self.near = 0.1
- self.far = 1000.0
-
- # Is the widget allowed to autospin?
- self.autospin_allowed = 0
-
- # Is the widget currently autospinning?
- self.autospin = 0
-
- # Basic bindings for the virtual trackball
- self.bind('<Map>', self.tkMap)
- self.bind('<Expose>', self.tkExpose)
- self.bind('<Configure>', self.tkExpose)
- self.bind('<Shift-Button-1>', self.tkHandlePick)
- #self.bind('<Button-1><ButtonRelease-1>', self.tkHandlePick)
- self.bind('<Button-1>', self.tkRecordMouse)
- self.bind('<B1-Motion>', self.tkTranslate)
- self.bind('<Button-2>', self.StartRotate)
- self.bind('<B2-Motion>', self.tkRotate)
- self.bind('<ButtonRelease-2>', self.tkAutoSpin)
- self.bind('<Button-3>', self.tkRecordMouse)
- self.bind('<B3-Motion>', self.tkScale)
-
-
- def help(self):
- """Help for the widget."""
-
- import Dialog
- d = Dialog.Dialog(None, {'title': 'Viewer help',
- 'text': 'Button-1: Translate\n'
- 'Button-2: Rotate\n'
- 'Button-3: Zoom\n'
- 'Reset: Resets transformation to identity\n',
- 'bitmap': 'questhead',
- 'default': 0,
- 'strings': ('Done', 'Ok')})
-
-
- def activate(self):
- """Cause this Opengl widget to be the current destination for drawing."""
-
- self.tk.call(self._w, 'makecurrent')
-
-
- # This should almost certainly be part of some derived class.
- # But I have put it here for convenience.
- def basic_lighting(self):
- """\
- Set up some basic lighting (single infinite light source).
-
- Also switch on the depth buffer."""
-
- self.activate()
- light_position = (1, 1, 1, 0)
- glLightfv(GL_LIGHT0, GL_POSITION, light_position)
- glEnable(GL_LIGHTING)
- glEnable(GL_LIGHT0)
- glDepthFunc(GL_LESS)
- glEnable(GL_DEPTH_TEST)
- glMatrixMode(GL_MODELVIEW)
- glLoadIdentity()
-
-
- def report_opengl_errors(message = "OpenGL error:"):
- """Report any opengl errors that occured while drawing."""
-
- print 'report_opengl_errors is now useless. glGetError replaced by GLexception'
- # while 1:
- # err_value = glGetError()
- # if not err_value: break
- # print message, gluErrorString(err_value)
-
-
- def set_background(self, r, g, b):
- """Change the background colour of the widget."""
-
- self.r_back = r
- self.g_back = g
- self.b_back = b
-
- self.tkRedraw()
-
-
- def set_centerpoint(self, x, y, z):
- """Set the new center point for the model.
- This is where we are looking."""
-
- self.xcenter = x
- self.ycenter = y
- self.zcenter = z
-
- self.tkRedraw()
-
-
- def set_eyepoint(self, distance):
- """Set how far the eye is from the position we are looking."""
-
- self.distance = distance
- self.tkRedraw()
-
-
- def reset(self):
- """Reset rotation matrix for this widget."""
-
- glMatrixMode(GL_MODELVIEW)
- glLoadIdentity()
- self.tkRedraw()
-
-
- def tkHandlePick(self, event):
- """Handle a pick on the scene."""
-
- if hasattr(self, 'pick'):
- # here we need to use glu.UnProject
-
- # Tk and X have their origin top left,
- # while Opengl has its origin bottom left.
- # So we need to subtract y from the window height to get
- # the proper pick position for Opengl
-
- realy = self.winfo_height() - event.y
-
- p1 = gluUnProject(event.x, realy, 0.)
- p2 = gluUnProject(event.x, realy, 1.)
-
- if self.pick(self, p1, p2):
- """If the pick method returns true we redraw the scene."""
-
- self.tkRedraw()
-
-
- def tkRecordMouse(self, event):
- """Record the current mouse position."""
-
- self.xmouse = event.x
- self.ymouse = event.y
-
-
- def StartRotate(self, event):
- # Switch off any autospinning if it was happening
-
- self.autospin = 0
- self.tkRecordMouse(event)
-
-
- def tkScale(self, event):
- """Scale the scene. Achieved by moving the eye position.
-
- Dragging up zooms in, while dragging down zooms out
- """
- scale = 1 - 0.01 * (event.y - self.ymouse)
- # do some sanity checks, scale no more than
- # 1:1000 on any given click+drag
- if scale < 0.001:
- scale = 0.001
- elif scale > 1000:
- scale = 1000
- self.distance = self.distance * scale
- self.tkRedraw()
- self.tkRecordMouse(event)
-
-
- def do_AutoSpin(self):
- s = 0.5
- self.activate()
-
- glRotateScene(0.5, self.xcenter, self.ycenter, self.zcenter, self.yspin, self.xspin, 0, 0)
- self.tkRedraw()
-
- if self.autospin:
- self.after(10, self.do_AutoSpin)
-
-
- def tkAutoSpin(self, event):
- """Perform autospin of scene."""
-
- self.after(4)
- self.update_idletasks()
-
- # This could be done with one call to pointerxy but I'm not sure
- # it would any quicker as we would have to split up the resulting
- # string and then conv
-
- x = self.tk.getint(self.tk.call('winfo', 'pointerx', self._w))
- y = self.tk.getint(self.tk.call('winfo', 'pointery', self._w))
-
- if self.autospin_allowed:
- if x != event.x_root and y != event.y_root:
- self.autospin = 1
-
- self.yspin = x - event.x_root
- self.xspin = y - event.y_root
-
- self.after(10, self.do_AutoSpin)
-
-
- def tkRotate(self, event):
- """Perform rotation of scene."""
-
- self.activate()
- glRotateScene(0.5, self.xcenter, self.ycenter, self.zcenter, event.x, event.y, self.xmouse, self.ymouse)
- self.tkRedraw()
- self.tkRecordMouse(event)
-
-
- def tkTranslate(self, event):
- """Perform translation of scene."""
-
- self.activate()
-
- # Scale mouse translations to object viewplane so object tracks with mouse
- win_height = max( 1,self.winfo_height() )
- obj_c = ( self.xcenter, self.ycenter, self.zcenter )
- win = gluProject( obj_c[0], obj_c[1], obj_c[2])
- obj = gluUnProject( win[0], win[1] + 0.5 * win_height, win[2])
- dist = math.sqrt( v3distsq( obj, obj_c ) )
- scale = abs( dist / ( 0.5 * win_height ) )
-
- glTranslateScene(scale, event.x, event.y, self.xmouse, self.ymouse)
- self.tkRedraw()
- self.tkRecordMouse(event)
-
-
- def tkRedraw(self, *dummy):
- """Cause the opengl widget to redraw itself."""
-
- if not self.initialised: return
- self.activate()
-
- glPushMatrix() # Protect our matrix
- self.update_idletasks()
- self.activate()
- w = self.winfo_width()
- h = self.winfo_height()
- glViewport(0, 0, w, h)
-
- # Clear the background and depth buffer.
- glClearColor(self.r_back, self.g_back, self.b_back, 0.)
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
-
- glMatrixMode(GL_PROJECTION)
- glLoadIdentity()
- gluPerspective(self.fovy, float(w)/float(h), self.near, self.far)
-
- if 0:
- # Now translate the scene origin away from the world origin
- glMatrixMode(GL_MODELVIEW)
- mat = glGetDoublev(GL_MODELVIEW_MATRIX)
- glLoadIdentity()
- glTranslatef(-self.xcenter, -self.ycenter, -(self.zcenter+self.distance))
- glMultMatrixd(mat)
- else:
- gluLookAt(self.xcenter, self.ycenter, self.zcenter + self.distance,
- self.xcenter, self.ycenter, self.zcenter,
- 0., 1., 0.)
- glMatrixMode(GL_MODELVIEW)
-
- # Call objects redraw method.
- self.redraw(self)
- glFlush() # Tidy up
- glPopMatrix() # Restore the matrix
-
- self.tk.call(self._w, 'swapbuffers')
- def redraw( self, *args, **named ):
- """Prevent access errors if user doesn't set redraw fast enough"""
-
-
- def tkMap(self, *dummy):
- """Cause the opengl widget to redraw itself."""
-
- self.tkExpose()
-
-
- def tkExpose(self, *dummy):
- """Redraw the widget.
- Make it active, update tk events, call redraw procedure and
- swap the buffers. Note: swapbuffers is clever enough to
- only swap double buffered visuals."""
-
- self.activate()
- if not self.initialised:
- self.basic_lighting()
- self.initialised = 1
- self.tkRedraw()
-
-
- def tkPrint(self, file):
- """Turn the current scene into PostScript via the feedback buffer."""
-
- self.activate()
-