-
Notifications
You must be signed in to change notification settings - Fork 0
/
Context.py
406 lines (344 loc) · 15.1 KB
/
Context.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
from OpenGL.GL import *
from OpenGL.GLU import *
import Select
from mouse import *
class EventReport:
'''Reports the results of dealing with an event'''
def __init__( self, handled=False, visDirty=False, sceneUpdate=False ):
# indicates that the event has been handled
self.isHandled = handled
# indicates that the event caused something that requires a redraw
self.needsRedraw = visDirty
# indicates that the event caused something that requires a scene
# update -- a scene update automatically implies a redraw
self.sceneUpdate = sceneUpdate
def __str__( self ):
return 'EventReport: handled( %s ), redraw( %s ), update( %s )' % ( self.isHandled, self.needsRedraw, self.sceneUpdate )
def set( self, handled, visDirty, update ):
'''Sets both variables in a single call'''
self.isHandled = handled
self.needsRedraw = visDirty
self.sceneUpdate = update
def combine( self, report ):
'''Combines this report with another report'''
self.isHandled = report.isHandled or self.isHandled
self.needsRedraw = report.needsRedraw or self.needsRedraw
self.sceneUpdate = report.sceneUpdate or self.sceneUpdate
class Context( object ):
'''Base class for UI contexts'''
def __init__( self ):
self.showHelp = False
def activate( self ):
'''Called when the context is activated'''
pass
def deactivate( self ):
'''Called when the context is deactivated'''
pass
def displayHelp( self ):
'''Displays help information for the context in the view'''
pass
def mousePressEvent( self, event, camControl, scene ):
'''Handle the mouse press event, returning an EventReport.
@param: event The event.
@param: camControl The scene's camera control for this event.
@param: scene The scene.
@returns: An instance of EventReport.
'''
return EventReport()
def mouseReleaseEvent( self, event, camControl, scene ):
'''Handle the mouse release event, returning an EventReport.
@param: event The event.
@param: camControl The scene's camera control for this event.
@param: scene The scene.
@returns: An instance of EventReport.
'''
return EventReport()
def mouseMoveEvent( self, event, camControl, scene ):
'''Handle the mouse move event, returning an EventReport.
@param: event The event.
@param: camControl The scene's camera control for this event.
@param: scene The scene.
@returns: An instance of EventReport.
'''
return EventReport()
def keyPressEvent( self, event, camControl ):
'''Handles key press events.
@param: event The key event.
@param: camControl The camera control for the view with this context.
@returns: An event report indicating what the contexts response to the event was.
'''
return EventReport()
def keyReleaseEvent( self, event, camControl ):
'''Handles key release events.
@param: event The key event.
@param: camControl The camera control for the view with this context.
@returns: An event report indicating what the contexts response to the event was.
'''
return EventReport()
def drawGL( self, camControl ):
'''Draw the the context's visual apparatus to the gl view'''
if ( self.showHelp ):
self.displayHelp()
self.draw3DGL( camControl, Select.SelectState.DRAW )
# set up UIGL
glMatrixMode( GL_PROJECTION )
glPushMatrix()
glLoadIdentity()
glOrtho( 0.0, camControl.VWIDTH - 1.0, 0.0, camControl.VHEIGHT - 1.0, -1.0, 1.0 )
glMatrixMode( GL_MODELVIEW )
glPushMatrix()
glLoadIdentity()
self.drawUIGL( camControl, Select.SelectState.DRAW )
glPopMatrix()
glMatrixMode( GL_PROJECTION )
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
def setAppRunning( self, state ):
'''The context is informed when the application is running'''
pass
def drawUIGL( self, camControl, select=False ):
'''Draws the UI elements (widgets, overlays, etc.) to the view.
@param: camControl The scene's camera control
@param: select Indicator if this draw call is made for selection purposes
'''
pass
def draw3DGL( self, camControl, select=False ):
'''Draws the 3D UI elements to the view.
@param: camControl The scene's camera control
@param: select Indicator if this draw call is made for selection purposes
'''
pass
def getName( self ):
'''Returns the name of the context'''
return self.__class__.__name__
def initGL( self ):
'''Gives the context a chance to initialize GL objects for a new context.'''
pass
class SelectContext( Context ):
'''A context which handles in scene selection'''
def __init__( self ):
Context.__init__( self )
self.downPoint = None
self.currPoint = None
self.selecting = False
def drawUIGL( self, camControl, select=False ):
'''Draws the UI elements (widgets, overlays, etc.) to the view.
@param: camControl The scene's camera control
@param: select Indicator if this draw call is made for selection purposes
'''
if ( not self.currPoint is None ):
# draw a rectangle from downPoint to currPoint
glPushAttrib( GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT )
glDisable( GL_LIGHTING )
glLineWidth( 1.0 )
# TODO: Translate this to screen space
glColor3f( 0.8, 0.8, 0.8 )
glBegin( GL_LINE_STRIP )
glVertex3f( self.downPoint[0], self.downPoint[1], 0.0 )
glVertex3f( self.downPoint[0], self.currPoint[1], 0.0 )
glVertex3f( self.currPoint[0], self.currPoint[1], 0.0 )
glVertex3f( self.currPoint[0], self.downPoint[1], 0.0 )
glVertex3f( self.downPoint[0], self.downPoint[1], 0.0 )
glEnd()
glPopAttrib()
def mousePressEvent( self, event, camControl, scene ):
'''Handle the mouse press event, returning an EventReport.
@param: event The event.
@param: camControl The scene's camera control for this event.
@param: scene The scene.
@returns: An instance of EventReport.
'''
result = EventReport()
hasCtrl, hasAlt, hasShift = getModState()
btn = event.button()
if ( btn == LEFT_BTN and not hasAlt ):
self.downPoint = ( event.x(), camControl.VHEIGHT - event.y() )
self.selecting = True
result.isHandled = True
return result
def mouseMoveEvent( self, event, camControl, scene ):
'''Handle the mouse move event, returning an EventReport.
@param: event The event.
@param: camControl The scene's camera control for this event.
@param: scene The scene.
@returns: An instance of EventReport.
'''
result = EventReport()
if ( self.selecting ):
result.isHandled = True
x = event.x()
y = camControl.VHEIGHT - event.y()
if ( x - self.downPoint[ 0 ] or y - self.downPoint[ 1 ] ):
self.currPoint = ( x, y )
result.needsRedraw = True
else:
self.currPoint = None
return result
def mouseReleaseEvent( self, event, camControl, scene ):
'''Handle the mouse press event, returning an EventReport
@param: event The event.
@param: camControl The scene's camera control for this event.
@param: scene The scene.
@returns: An instance of EventReport.
'''
result = EventReport( True )
# if not handled, either perform selection or view manipulation
hasCtrl, hasAlt, hasShift = getModState()
btn = event.button()
if ( btn == LEFT_BTN and not hasAlt ):
self.selecting = False
if ( self.currPoint is None ):
# TODO: Eventually shift this to allow for a selection REGION
selected = self.selectSingle( scene, camControl, ( event.x(), event.y() ) )
else:
selected = self.selectRegion( scene, camControl, self.downPoint, self.currPoint )
# Need to stop drawing the selection region
result.needsRedraw = True
if ( selected ):
if ( hasShift and hasCtrl ):
# add
result.needsRedraw |= Select.addToGlobalSelection( selected ) > 0
elif ( hasShift ):
# toggle
result.needsRedraw |= Select.toggleGlobalSelection( selected ) > 0
elif ( hasCtrl ):
# remove
result.needsRedraw |= Select.removeFromGlobalSelection( selected ) > 0
else:
# replace
result.needsRedraw |= Select.setGlobalSelection( selected ) > 0
else:
if ( not ( hasShift or hasCtrl ) ):
result.needsRedraw |= Select.clearGlobalSelection() > 0
self.currPoint = self.downPoint = None
if ( result.needsRedraw ):
self.selectionChanged()
return result
def selectSingle( self, scene, camControl, selectPoint, pickSize=5 ):
'''Given the scene, camera and a selection point in screen space,
performs selection and returns the single front-most selectable under the mouse.
@param: scene The scene.
@param: camControl The camera control.
@param: selectPoint The screen space point at which selection is to be done.
@param: pickSize The size of the selection box around the selectPoint.
@returns: The selected object.
'''
glPushAttrib( GL_ENABLE_BIT )
glDisable( GL_LIGHTING )
glDisable( GL_TEXTURE_2D )
glMatrixMode( GL_PROJECTION )
glPushMatrix()
glMatrixMode( GL_MODELVIEW )
glPushMatrix()
camControl.setSelectMat( selectPoint, pickSize )
camControl.setGLView()
## Select.start()
## self.drawUIGL( Select.SelectState.SELECT )
## selected = Select.endSingle()
selected = None
if ( selected is None ):
Select.start()
self.draw3DGL( camControl, Select.SelectState.SELECT )
selected = Select.endSingle()
if ( selected is None and scene is not None ):
Select.start()
scene.drawTreeGL( Select.SelectState.SELECT )
selected = Select.endSingle()
glMatrixMode( GL_PROJECTION )
glPopMatrix()
glMatrixMode( GL_MODELVIEW )
glPopMatrix()
glPopAttrib()
return selected
def selectRegion( self, scene, camControl, point0, point1 ):
'''Given the scene, camera and a selection *region*, returns a set of all
selectables which intersect the region.
@param: scene The scene.
@param: camControl The camera control.
@param: point0 The x-y coordintes of one corner of the rectangular region.
@param: point1 The x-y coordintes of the other corner of the rectangular region.
@returns: The selected object.
'''
x0 = min( point0[0], point1[0] )
x1 = max( point0[0], point1[0] )
y0 = min( point0[1], point1[1] )
y1 = max( point0[1], point1[1] )
glPushAttrib( GL_ENABLE_BIT )
glDisable( GL_LIGHTING )
glDisable( GL_TEXTURE_2D )
glMatrixMode( GL_PROJECTION )
glPushMatrix()
glMatrixMode( GL_MODELVIEW )
glPushMatrix()
camControl.setSelectMatRegion( x0, y0, x1, y1 )
camControl.setGLView()
## Select.start()
## self.drawUIGL( Select.SelectState.SELECT )
## newSelection = Select.end()
newSelection = None
if ( newSelection == None ):
## Select.start()
## self.draw3DGL( Select.SelectState.SELECT )
## newSelection = Select.end()
if ( newSelection == None ):
Select.start()
scene.drawTreeGL( Select.SelectState.SELECT )
selected = Select.endSet()
glMatrixMode( GL_PROJECTION )
glPopMatrix()
glMatrixMode( GL_MODELVIEW )
glPopMatrix()
glPopAttrib()
return selected
def selectionChanged( self ):
'''Callback for when the selection changes'''
pass
class ContextSwitcher( Context ):
'''Class for switching between contexts through keyboard short-cuts'''
def __init__( self ):
Context.__init__( self )
self.contexts = {}
self.activeContext = None
def addContext( self, context, key ):
'''Add a context to the switcher, tied to a key.
If two contexts are given the same key binding, the latter
binding is valid.'''
if ( self.contexts.has_key( key ) ):
print "Old context, %s, replaced with %s" % ( self.contexts[ key ].getName(), context.getName() )
self.contexts[ key ] = context
def switchContexts( self, context ):
'''Switches the active context to the given context'''
if ( self.activeContext ):
self.activeContext.deactivate()
self.activeContext = context
if ( context ):
context.activate()
def handleKeyboard( self, event ):
'''Handle the keyboard event, returning an EventReport'''
hasCtrl, hasAlt, hasShift = getModState()
if ( not ( hasCtrl or hasAlt or hasShift ) ):
if ( event.key == pygame.K_ESCAPE ):
if ( self.activeContext ):
self.switchContexts( None )
return EventReport( True, True )
if ( self.contexts.has_key( event.key ) ):
self.switchContexts( self.contexts[ event.key ] )
return EventReport( True, True )
if ( self.activeContext ):
return self.activeContext.handleKeyboard( event )
return EventReport()
def handleMouse( self, event ):
'''Handle the mouse event, returning an EventReport'''
if ( self.activeContext, camera, scene ):
return self.activeContext.handleMouse( event, camera, scene )
else:
return EventReport()
def drawGL( self ):
if ( self.activeContext ):
self.activeContext.drawGL()
else:
Context.drawGL( self )
def setAppRunning( self, state ):
'''The context is informed when the application is running'''
for ctx in self.contexts.items():
ctx.setAppRunning( state )