"""Defines GLWidgetPlugin, GLMultiViewportProgram, and CachedGLObject, which
are used by the core visualization module. They may be useful for writing
your own GLPluginInterface classes, too.
"""
from . import glinit
from OpenGL import GL
from .glinterface import GLPluginInterface
from .glprogram import GLProgram,GLPluginProgram
import math
import weakref
import warnings
[docs]class GLMultiViewportProgram(GLProgram):
"""A GLProgram that splits the window into several sub-views. Each view is
a GLProgram or GLPluginInterface.
"""
def __init__(self):
GLProgram.__init__(self)
self.views = []
self.activeView = None
self.dragging = False
self.sizePolicy = 'fit'
self.broadcast = False
self.defaultSizes = []
#self.height = self.view.h
[docs] def initialize(self):
if not GLProgram.initialize(self): return False
for v in self.views:
v.window = self.window
if not v.initialize():
return False
return True
[docs] def addView(self,view):
warnings.warn("addView will be deprecated in favor of add_view in a future version of Klampt",DeprecationWarning)
self.add_view(view)
[docs] def removeView(self,view):
warnings.warn("removeView will be deprecated in favor of remove_view in a future version of Klampt",DeprecationWarning)
self.remove_view(view)
[docs] def clearViews(self):
warnings.warn("clearViews will be deprecated in favor of clear_views in a future version of Klampt",DeprecationWarning)
self.clear_views()
def update_active(self,x,y):
warnings.warn("update_active will be deprecated in favor of update_active in a future version of Klampt",DeprecationWarning)
self.update_active(x,y)
[docs] def add_view(self,view):
if isinstance(view,GLPluginInterface):
plugin = view
pview = GLPluginProgram()
pview.window = self.window
pview.set_plugin(view)
view = pview
assert isinstance(view,GLProgram)
self.views.append(view)
#spoofs reshape, motion functions
view.window = weakref.proxy(self)
self.defaultSizes.append((view.view.w,view.view.h))
self.fit()
#print "Added a view, total",len(self.views),"size now",self.view.w,self.view.h
return view
[docs] def remove_view(self,view):
view.window = None
for i,p in enumerate(self.views):
if p is view:
self.views.pop(i)
self.defaultSizes.pop(i)
self.fit()
self.activeView = None
return
[docs] def clear_views(self):
for p in self.views:
p.window = None
self.views = []
self.defaultSizes = []
self.activeView = None
[docs] def update_active(self,x,y):
if not self.view.contains(x,y):
return
self.activeView = None
for i,p in enumerate(self.views):
if p.view.contains(x,y):
#print "Selecting view",x,y,":",i
self.activeView = i
return
return
[docs] def fit(self):
if len(self.views) == 0: return
rowlen = int(math.ceil(math.sqrt(len(self.views))))
assert rowlen > 0
rowheights = [0]*int(math.ceil(float(len(self.views))/rowlen))
colwidths = [0]*rowlen
for i,p in enumerate(self.views):
col = i % rowlen
row = int(i / rowlen)
rowheights[row] = max(self.defaultSizes[i][1],rowheights[row])
colwidths[col] = max(self.defaultSizes[i][0],colwidths[col])
cumrowheights = [0]
cumcolwidths = [0]
for h in rowheights:
cumrowheights.append(cumrowheights[-1]+h)
for w in colwidths:
cumcolwidths.append(cumcolwidths[-1]+w)
if self.sizePolicy == 'fit':
self.view.w = sum(colwidths)
self.view.h = sum(rowheights)
for i,p in enumerate(self.views):
col = i % rowlen
row = int(i / rowlen)
p.view.x,p.view.y = (cumcolwidths[col],cumrowheights[row])
self.width = self.view.w
self.height = self.view.h
if self.window != None:
self.window.reshape(self.view.w,self.view.h)
else:
#squeeze
self.width = self.view.w
self.height = self.view.h
for i,p in enumerate(self.views):
col = i % rowlen
row = int(i / rowlen)
p.view.screenDeviceScale = self.view.screenDeviceScale
p.view.x = float(self.view.w)*float(cumcolwidths[col])/float(cumcolwidths[-1])
p.view.y = float(self.view.h)*float(cumrowheights[row])/float(cumrowheights[-1])
p.view.w = float(self.view.w)*float(colwidths[col]) / float(cumcolwidths[-1])
p.view.h = float(self.view.h)*float(rowheights[row]) / float(cumrowheights[-1])
p.view.x = self.view.x+int(p.view.x)
p.view.y = self.view.y+int(p.view.y)
p.view.w = int(p.view.w)
p.view.h = int(p.view.h)
#print "View",i,"shape",(p.view.x,p.view.y,p.view.w,p.view.h)
p.reshapefunc(p.view.w,p.view.h)
if self.window != None:
self.refresh()
[docs] def reshapefunc(self,w,h):
if (w,h) != (self.view.w,self.view.h):
self.view.w,self.view.h = w,h
self.height = self.view.h
self.sizePolicy = 'squash'
self.fit()
return True
[docs] def displayfunc(self):
anyTrue = False
GL.glClearColor(0,0,0,0)
GL.glScissor(0,0,self.view.w*self.view.screenDeviceScale,self.view.h*self.view.screenDeviceScale)
GL.glEnable(GL.GL_SCISSOR_TEST);
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
for p in self.views:
try:
if p.displayfunc():
anyTrue = True
except Exception:
warnings.warn("Error running displayfunc() for plugin {}".format(p.__class__.__name__))
raise
return anyTrue
[docs] def display(self):
anyTrue = False
for p in self.views:
try:
if p.display():
anyTrue = True
except Exception:
warnings.warn("Error running display() for plugin {}".format(p.__class__.__name__))
raise
return anyTrue
[docs] def display_screen(self):
anyTrue = False
for p in self.views:
try:
if p.display_screen():
anyTrue = True
except Exception:
print("Error running display_screen() for plugin",p.__class__.__name__)
raise
return anyTrue
[docs] def keyboardfunc(self,c,x,y):
if self.broadcast:
for p in self.views:
p.keyboardfunc(c,x,y)
return True
self.update_active(x,y)
if self.activeView != None:
return True if self.views[self.activeView].keyboardfunc(c,x,y) else False
return False
[docs] def keyboardupfunc(self,c,x,y):
if self.broadcast:
for p in self.views:
p.keyboardupfunc(c,x,y)
return True
self.update_active(x,y)
if self.activeView != None:
return True if self.views[self.activeView].keyboardupfunc(c,x,y) else False
return False
[docs] def mousefunc(self,button,state,x,y):
if self.broadcast:
for p in self.views:
p.mousefunc(button,state,x,y)
return True
if state == 0:
#button down
self.update_active(x,y)
self.dragging = True
else:
self.dragging = False
if self.activeView != None:
return True if self.views[self.activeView].mousefunc(button,state,x,y) else False
return False
[docs] def motionfunc(self,x,y,dx,dy):
if self.broadcast:
for p in self.views:
p.motionfunc(x,y,dx,dy)
return True
if not self.dragging:
self.update_active(x,y)
if self.activeView != None:
return True if self.views[self.activeView].motionfunc(x,y,dx,dy) else False
return False
[docs] def idlefunc(self):
for p in self.views:
p.idlefunc()
return True
[docs] def refresh(self):
"""Spoofs the window's refresh function"""
self.window.refresh()
[docs] def reshape(self,w,h):
"""Spoofs the window's reshape function"""
raise NotImplementedError("Can't have a viewport reshaping a multi-viewport yet")
self.sizePolicy = 'squash'
self.fit()
return True
[docs] def draw_text(self,point,text,size=12,color=None):
"""Draws text of the given size and color at the given point. Usually
called during display_screen."""
#if self.activeView == None:
self.window.draw_text(point,text,size,color)
#the GL viewport should be setup already
#else:
# ox,oy = self.pluginOrigins[self.activeView]
# self.window.draw_text(ox+x,oy+y,text,size,color)
[docs] def click_ray(self,x,y):
#print "Getting click ray"
if self.activeView == None:
return self.window.click_ray(x,y)
else:
return self.views[self.activeView].click_ray(x,y)
[docs] def get_view(self):
#print "Getting viewport..."
if self.activeView == None:
return self.window.get_view()
else:
return self.views[self.activeView].get_view()
[docs] def set_view(self,vp):
#print "Getting viewport..."
if self.activeView == None:
return self.window.set_view(vp)
else:
return self.views[self.activeView].set_view(vp)
_CACHED_DISPLAY_LISTS = set()
_CACHED_WARN_THRESHOLD = 1000
_CACHED_DELETED_LISTS = list()
[docs]class CachedGLObject:
"""An object whose drawing is accelerated by means of a display list.
The draw function may draw the object in the local frame, and the
object may be transformed without having to recompile the display list.
"""
def __init__(self):
self.name = ""
#OpenGL display list
self.glDisplayList = None
#marker for recursive calls
self.makingDisplayList = False
#parameters for render function
self.displayListRenderArgs = None
#other parameters for display lists
self.displayListParameters = None
#dirty bit to indicate whether the display list should be recompiled
self.changed = False
def __del__(self):
self.destroy()
[docs] def destroy(self):
"""Must be called to free up resources used by this object"""
if self.glDisplayList != None:
global _CACHED_DELETED_LISTS,_CACHED_DISPLAY_LISTS
if len(_CACHED_DELETED_LISTS) > 100:
for dl in _CACHED_DELETED_LISTS:
GL.glDeleteLists(dl,1)
_CACHED_DELETED_LISTS = list()
else:
_CACHED_DELETED_LISTS.append(self.glDisplayList)
_CACHED_DISPLAY_LISTS.remove(self.glDisplayList)
self.glDisplayList = None
[docs] def markChanged(self):
warnings.warn("markChanged will be deprecated in favor of mark_changed in a future version of Klampt",DeprecationWarning)
self.mark_changed()
[docs] def mark_changed(self):
"""Marked by an outside source to indicate the object has changed and
should be redrawn."""
self.changed = True
[docs] def draw(self,renderFunction,transform=None,args=None,parameters=None):
"""Given the function that actually makes OpenGL calls, this
will draw the object.
If parameters is given, the object's local appearance is assumed
to be defined deterministically from these parameters. The display
list will be redrawn if the parameters change.
"""
from ..math import se3
if args == None:
args = ()
if self.makingDisplayList:
renderFunction(*args)
return
if self.glDisplayList == None or self.changed or parameters != self.displayListParameters or args != self.displayListRenderArgs:
self.displayListRenderArgs = args
self.displayListParameters = parameters
self.changed = False
if self.glDisplayList == None:
#print "Generating new display list",self.name
global _CACHED_WARN_THRESHOLD,_CACHED_DISPLAY_LISTS,_CACHED_DELETED_LISTS
if len(_CACHED_DELETED_LISTS) > 0:
self.glDisplayList = _CACHED_DELETED_LISTS[-1]
_CACHED_DELETED_LISTS.pop(-1)
else:
self.glDisplayList = GL.glGenLists(1)
_CACHED_DISPLAY_LISTS.add(self.glDisplayList)
if len(_CACHED_DISPLAY_LISTS) > _CACHED_WARN_THRESHOLD:
print("GLCachedObject: Creating",len(_CACHED_DISPLAY_LISTS),"GL objects",self.glDisplayList,"watch me for memory usage...")
_CACHED_WARN_THRESHOLD += 1000
#print "Compiling display list",self.name
if transform:
GL.glPushMatrix()
GL.glMultMatrixf(sum(list(zip(*se3.homogeneous(transform))),()))
GL.glNewList(self.glDisplayList,GL.GL_COMPILE_AND_EXECUTE)
self.makingDisplayList = True
try:
renderFunction(*args)
except GL.GLError:
import traceback
print("Error encountered during draw, display list",self.glDisplayList)
traceback.print_exc()
self.makingDisplayList = False
GL.glEndList()
if transform:
GL.glPopMatrix()
else:
if transform:
GL.glPushMatrix()
GL.glMultMatrixf(sum(list(zip(*se3.homogeneous(transform))),()))
GL.glCallList(self.glDisplayList)
if transform:
GL.glPopMatrix()