-
Notifications
You must be signed in to change notification settings - Fork 0
/
Select.py
171 lines (144 loc) · 5.13 KB
/
Select.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
# Class for handling selection
from OpenGL.GL import *
# all registered selectable objects
SELECTABLES = [None]
# The currently selected set
GLOBAL_SELECTION = set()
# the draw state of a selectable
class SelectState:
DRAW = 0
## HOVER = 1
SELECT = 2
class Selectable:
'''Base class for selectable objects - using OpenGL selection mechanism.'''
# The global identifiers for selectable objects.
ID = 1
def __init__( self ):
'''Constructor. '''
self.id = Selectable.ID
self.selected = SelectState.DRAW
Selectable.ID += 1
SELECTABLES.append( self )
def glName( self ):
'''Put the name into a gl context.'''
glLoadName( self.id )
def setSelected( self ):
'''Sets the state of this node to be selected.'''
self.selected = SelectState.SELECT
def clearSelected( self ):
'''Sets the state of this node to be UNselected.'''
self.selected = SelectState.DRAW
BUFFER_SIZE = 1024
def addToGlobalSelection( selectSet ):
'''Adds a selectable to the global selection set.
@param: selectSet A set of selectable elements to add.
@returns: The number of selectables added. Should be zero or one.
'''
global GLOBAL_SELECTION
new = selectSet.difference( GLOBAL_SELECTION )
newCount = len( new )
if ( newCount ):
GLOBAL_SELECTION |= new
for item in new:
item.setSelected()
return newCount
def toggleGlobalSelection( selectSet ):
'''Toggles the selectables in the given set in the global selection.
@param: selectSet A set of selectables whose state should be toggled.
@returns: The number of selectables whose selection state is changed.
'''
global GLOBAL_SELECTION
deselect = selectSet & GLOBAL_SELECTION
select = selectSet - GLOBAL_SELECTION
for item in deselect:
item.clearSelected()
for item in select:
item.setSelected()
GLOBAL_SELECTION |= select
GLOBAL_SELECTION -= deselect
return len( deselect ) + len( select )
def removeFromGlobalSelection( selectSet ):
'''Removes the selectables in the given set from the global selection.
@param: selectSet A set of selectables who should be deselected.
@returns: The number of selectables actually deselected.
'''
global GLOBAL_SELECTION
deselect = selectSet & GLOBAL_SELECTION
for item in deselect:
item.clearSelected()
GLOBAL_SELECTION -= deselect
return len( deselect )
def setGlobalSelection( selectSet ):
'''Removes the selectables in the given set from the global selection.
@param: selectSet A set of selectables who should be deselected.
@returns: The number of selectables actually deselected.
'''
global GLOBAL_SELECTION
deselect = GLOBAL_SELECTION - selectSet
select = selectSet - GLOBAL_SELECTION
for item in deselect:
item.clearSelected()
for item in select:
item.setSelected()
GLOBAL_SELECTION -= deselect
GLOBAL_SELECTION |= select
return len( deselect ) + len( select )
def clearGlobalSelection():
'''Clears the selection set.'''
count = len( GLOBAL_SELECTION )
for s in GLOBAL_SELECTION:
s.clearSelected()
GLOBAL_SELECTION.clear()
return count
def start():
'''Starts the selection process.'''
glSelectBuffer( BUFFER_SIZE )
glRenderMode( GL_SELECT )
glInitNames()
glPushName( 0 )
def endSingle():
'''Ends the selection process for selecting the *single* front-most element.
@returns: A set containing the selected element.
'''
hits = glRenderMode( GL_RENDER )
# this implicitly does single selection
selected = closestHit( hits )
if ( selected ):
return set( [selected] ) #{ selected } # returning a set
else:
return set()
def endSet():
'''Ends the selection process for selecting the *single* front-most element.
@returns: A set containing the selected element.
'''
hits = glRenderMode( GL_RENDER )
return hitSet( hits )
def hitSet( buffer ):
'''Produces a set of all hit records.
@param: buffer The selection buffer produced by OpenGL.
@returns: A set consisting of all the corresponding selectables.
'''
return set( SELECTABLES[ record[2][0] ] for record in buffer if record[2][0] != 0)
def closestHit( buffer ):
'''Given the ids of the elements drawn in the selection buffer,
returns the Selectable who's depth was lowest (i.e., in front).
@param: buffer The selection buffer produced by OpenGL.
@returns: A reference to the single closest selectable.
'''
closest = -1
if ( len(buffer) == 1):
closest = buffer[0][2][0]
elif ( len( buffer ) > 1 ):
closestDist = buffer[0][0]
closest = buffer[0][2][0]
for hit in buffer[1:]:
testDist = hit[0]
if ( testDist < closestDist ):
closest = hit[2][0]
if ( closest > -1 ):
if ( closest < len( SELECTABLES ) ):
return SELECTABLES[ closest ]
else:
return closest
else:
return None