summaryrefslogtreecommitdiff
path: root/cad/src/graphics/drawables/ResizeHandle.py
blob: d87756da96ce753fe6eb4ed30a0240bc5c34c91c (plain)
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
# Copyright 2007-2008 Nanorex, Inc.  See LICENSE file for details. 
"""
ResizeHandle.py - provides Handles for resizing the parent object. The parent 
object in most cases is a reference geometry model object (e.g. Plane, Line etc) 

@author: Ninad
@version: $Id$
@copyright: 2007-2008 Nanorex, Inc.  See LICENSE file for details.

History:
Ninad 2007-10-16: Originally created and modified in Plane.py, while working
                  on implemention of resizable  Planes (in May-June 2007). 
                  Moved this class  to its own module and renamed it to 
                  'ResizeHandle' (the old name was 'Handle')
"""

from OpenGL.GL import glPushName
from OpenGL.GL import glPopName
from OpenGL.GL import glPushMatrix
from OpenGL.GL import glPopMatrix
from OpenGL.GL import glTranslatef
from OpenGL.GL import glRotatef

from graphics.drawing.drawers import drawLineLoop
from graphics.drawing.drawers import drawPlane

import foundation.env as env
from utilities.prefs_constants import selectionColor_prefs_key
from utilities.constants import black, orange

from math import pi
from geometry.VQT import V

from utilities.debug import print_compact_traceback

from graphics.drawables.DragHandler import DragHandler_API
from graphics.drawables.Selobj import Selobj_API

ONE_RADIAN = 180.0 / pi
# One radian = 57.29577951 degrees
# This is for optimization since this computation occurs repeatedly
# in very tight drawning loops. --Mark 2007-08-14

class ResizeHandle(DragHandler_API, Selobj_API):
    """
    This class provides Handles for resizing the parent object. The parent 
    object in most cases is a geometry (e.g. Plane, Line etc) 
    @see: L{Plane._draw_handles} 
    @Note: This is  unrelated with the things in handles.py
          
    """
    def __init__(self, parent, glpane, handleCenter):
        """
        Constructor for class ResizeHandle
        @param parent: The parent object that can be resized using this handle.
                       The parent can be a L{ReferenceGeometry}
        @param handleCenter: The center of the handle. If None, use the handle's
                        I{center} property.
        @type  handleCenter: L{V} or None
        
        """
        self.parent = parent
        #this could be parent.glpane , but pass glpane object explicitely
        #for creating  a handle to avoid any errors
        self.glpane = glpane
        self.center = handleCenter
        # No texture in handles (drawn ResizeHandle object). 
        # Ideally the following value in the drawer.drawPlane method 
        # should be False by default.
        self.textureReady  = False
        self.pickCheckOnly = False        
            ### REVIEW/TODO: understanding how self.pickCheckOnly might be
            # left over from one drawing call to another (potentially causing
            # bugs) is a mess. It needs to be refactored so that it's just an
            # argument to all methods it's passed through. This involves some
            # superclass methods; maybe they can be overridden so the argument
            # is only needed in local methods, I don't know. This is done in
            # several Node classes, so I added this comment to all of them.
            # (In this class, it looks like it's always False, but it's hard to
            #  be sure re subclasses and superclasses.)
            # [bruce 090310 comment]
        self.glname = glpane.alloc_my_glselect_name(self) #bruce 080917 revised
        self.type   = None      
    
    def draw(self, hCenter = None):
        """
        Public method to draw the resize handle. 
        @param hCenter: The center of the handle. If None, use the handle's 
                        I{center} property.
        @type  hCenter: L{V} or None
        @see: :{self._draw} which is called inside this method.
        """
        try:
            glPushName(self.glname)
            if hCenter:
                self._draw(hCenter)    
            else:
                self._draw()
        except:
            glPopName()
            print_compact_traceback(
                "ignoring exception when drawing handle %r: " % self)
        else:
            glPopName()
    
    def _draw(self, hCenter = None, highlighted = False):
        """
        Draw the resize handle. It does the actual drawing work. 
        
        @param hCenter: The center of the handle. If None, use the handle's 
                        I{center} property.
        @type  hCenter: L{V} or None
        
        @param highlighted: This argument determines if the handle is 
                            drawn in the highlighted color.
        @type  highlighted: bool
        @see: {self.draw} where this method is called. 
        """        
        
        if hCenter:
            if self.center != hCenter:
                self.center = hCenter    
                   
        #Use glpane's scale for drawing the handle. This makes sure that
        # the handle is non-zoomable. 
        side = self.glpane.scale * 0.018                
        glPushMatrix() 
        
        
        #Translate to the center of the handle
        glTranslatef(self.center[0], 
                     self.center[1], 
                     self.center[2])  
                        
        #Bruce suggested undoing the glpane.quat rotation and plane quat 
        #rotation  before drawing the handle geometry. -- ninad 20070525
        
        parent_q = self.parent.quat 
        
        if parent_q:
            glRotatef(-parent_q.angle * ONE_RADIAN,
                       parent_q.x, 
                       parent_q.y, 
                       parent_q.z)
            
        glpane_q = self.glpane.quat
        glRotatef(-glpane_q.angle * ONE_RADIAN, 
                   glpane_q.x, 
                   glpane_q.y, 
                   glpane_q.z) 
       
        drawPlane(env.prefs[selectionColor_prefs_key], 
                  side, 
                  side, 
                  self.textureReady,
                  0.9, 
                  SOLID         = True, 
                  pickCheckOnly = self.pickCheckOnly)         
        
        handle_hw = side/2.0 #handle's half width
        handle_hh = side/2.0 #handle's half height        
        handle_corner = [V(-handle_hw,  handle_hh, 0.0), 
                         V(-handle_hw, -handle_hh, 0.0), 
                         V( handle_hw, -handle_hh, 0.0), 
                         V( handle_hw,  handle_hh, 0.0)]   
        
        if highlighted:    
            drawLineLoop(orange, handle_corner, width = 6)
        else:
            drawLineLoop( black, handle_corner, width = 2)                
        glPopMatrix()
        
    def draw_in_abs_coords(self, glpane, color):
        """
        Draw the handle as a highlighted object.
        
        @param glpane: The 3D Graphics area.
        @type  gplane: L{GLPane}
        
        @param color: Unused.
        @type  color:
        
        @attention: I{color} is not used.
        """          
        q = self.parent.quat  
        glPushMatrix()
        if self.parent.center:
            glTranslatef( self.parent.center[0],
                          self.parent.center[1], 
                          self.parent.center[2])
        if q:
            glRotatef( q.angle * ONE_RADIAN, 
                       q.x,
                       q.y,
                       q.z)            
        
        self._draw(highlighted = True)
        glPopMatrix()
        
    def move(self, offset):
        """
        Move the handle by I{offset}.
        
        @param offset: The offset of the handle, in Angstroms.
        @type  offset: V
        """
        self.center += offset
    
    def setType(self, handleType):
        """
        Sets the handle type.
        @param handleType: The handle type. Must be one of:
                           'Width-Handle', 'Height-Handle','Corner' or 
                           just '' (type not specified)
        @type  handleType: str
        """
        assert handleType in [ 'Width-Handle', 'Height-Handle', 'Corner', '']
        self.type = handleType
       
    def getType(self):
        """
        Returns the handle type.
        
        @return: The handle type, which is either
                 'Width-Handle', 'Height-Handle', or 'Corner'.
        @rtype:  str
        """
        assert self.type is not None
        return self.type
        
    ###============== selobj interface Starts ===============###
     
    #Methods for selobj interface  . Note that draw_in_abs_coords method is 
    #already defined above.  -- Ninad 20070612
    
    #@TODO Need some documentation. Basically it implements the selobj 
    #interface mentioned in exprs.Highlightable.py
    
    def leftClick(self, point, event, mode):
        mode.handleLeftDown(self, event)
        mode.update_selobj(event)
        return self               

    def mouseover_statusbar_message(self):
        msg1 = "Parent:"
        msg2 = str(self.parent.name)        
        type = self.getType()
        if type:
            msg3 = " Type: "
        else:
            msg3 = ''
        msg4 = type
        
        return msg1 + msg2 + msg3 + msg4
        
    def highlight_color_for_modkeys(self, modkeys):
        return orange
    
    # Copied Bruce's code from class Highlightable with some mods. 
    # Need to see if selobj_still_ok() is needed. OK for now.
    # --Ninad 2007-05-31
    def selobj_still_ok(self, glpane):
        # bugfix: compare to correct class [bruce 070924]
        res = self.__class__ is ResizeHandle 
        if res:
            our_selobj = self
            glname     = self.glname
            owner      = glpane.assy.object_for_glselect_name(glname)
            if owner is not our_selobj:
                res = False
                # Do debug prints.
                print "%r no longer owns glname %r, instead %r does" \
                      % (self, glname, owner) #[perhaps never seen as of 061121]
            pass
        if not res and env.debug():
            print "debug: selobj_still_ok is false for %r" % self 
        return res
    ###============== selobj interface Ends ===============###
    
    ###=========== Drag Handler interface Starts =============###
    #@TODO Need some documentation. Basically it implements the drag handler 
    #interface described in DragHandler.py See also exprs.Highlightable.py
    
    def handles_updates(self): 
        return True
            
    def DraggedOn(self, event, mode): 
        mode.handleLeftDrag(self, event)
        mode.update_selobj(event)
        return
    
    def ReleasedOn(self, selobj, event, mode): 
        pass
    ###=========== Drag Handler interface Ends =============###