summaryrefslogtreecommitdiff
path: root/cad/src/temporary_commands/RotateAboutPoint_Command.py
blob: 69064e549124bc3afc8038030e4a79a8e7dd83c0 (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
295
296
# Copyright 2008-2009 Nanorex, Inc.  See LICENSE file for details.
"""

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

History:

TODO: 2008-04-20
- Created a support a NFR to rotate about a point just before FNANO 2008
conference. This may be revised further.
-- Need documentation
"""

from Numeric import dot
import math # for pi

from utilities.prefs_constants import atomHighlightColor_prefs_key
from utilities.constants import black
from utilities.prefs_constants import DarkBackgroundContrastColor_prefs_key
from utilities.debug import print_compact_stack, print_compact_traceback

from geometry.VQT import cross, norm, Q

import foundation.env as env

from graphics.drawing.CS_draw_primitives import drawline

from model.chem import Atom # for isinstance check as of 2008-04-17

from temporary_commands.LineMode.Line_Command import Line_Command
from temporary_commands.LineMode.Line_GraphicsMode import Line_GraphicsMode

# ==

_superclass_for_GM = Line_GraphicsMode

class RotateAboutPoint_GraphicsMode(Line_GraphicsMode):

    pivotPoint = None

    def Enter_GraphicsMode(self):
        #TODO: use this more widely,  than calling grapicsMode.resetVariables
        #in command.restore_GUI. Need changes in superclasses etc
        #-- Ninad 2008-04-17
        self.resetVariables() # For safety

    def Draw_other(self):
        """
        """
        _superclass_for_GM.Draw_other(self)

        if len(self.command.mouseClickPoints) >= 2:
            #Draw reference vector.
            drawline(env.prefs[DarkBackgroundContrastColor_prefs_key],
                     self.command.mouseClickPoints[0],
                     self.command.mouseClickPoints[1],
                     width = 4,
                     dashEnabled = True)


    def resetVariables(self):
        _superclass_for_GM.resetVariables(self)
        self.pivotPoint = None


    def _determine_pivotPoint(self, event):
        """
        Determine the pivot point about which to rotate the selection
        """
        self.pivotPoint = None
        selobj = self.glpane.selobj
        if isinstance(selobj, Atom):
            self.pivotPoint = selobj.posn()
        else:
            farQ_junk, self.pivotPoint = self.dragstart_using_GL_DEPTH( event)


    def leftDown(self, event):
        """
        Event handler for LMB press event.
        """
        #The endPoint1 and self.endPoint2 are the mouse points at the 'water'
        #surface. Soon, support will be added so that these are actually points
        #on a user specified reference plane. Also, once any temporary mode
        # begins supporting highlighting, we can also add feature to use
        # coordinates of a highlighted object (e.g. atom center) as endpoints
        # of the line
        selobj = self.glpane.selobj

        if len(self.command.mouseClickPoints) == 0:
            self._determine_pivotPoint(event)

        self.endPoint1 = self.pivotPoint

        if isinstance(selobj, Atom):
            mouseClickPoint = selobj.posn()
        else:
            if self.pivotPoint is not None:
                planeAxis = self.glpane.lineOfSight
                planePoint = self.pivotPoint
                mouseClickPoint = self.dragstart_using_plane_depth(
                        event,
                        planeAxis = planeAxis,
                        planePoint = planePoint)
            else:
                farQ_junk, mouseClickPoint = self.dragstart_using_GL_DEPTH( event)

        if self._snapOn and self.endPoint2 is not None:
            # This fixes a bug. Example: Suppose the dna line is snapped to a
            # constraint during the bare motion and the second point is clicked
            # when this happens, the second clicked point is the new
            #'self.endPoint1'  which needs to be snapped like we did for
            # self.endPoint2 in the bareMotion. Setting self._snapOn to False
            # ensures that the cursor is set to the simple Pencil cursor after
            # the click  -- Ninad 2007-12-04
            mouseClickPoint = self.snapLineEndPoint()
            self._snapOn = False

        self.command.mouseClickPoints.append(mouseClickPoint)
        return

    def leftUp(self, event):
        """
        Event handler for Left Mouse button left-up event
        @see: Line_Command._f_results_for_caller_and_prepare_for_new_input()
        """

        if len(self.command.mouseClickPoints) == 3:
            self.endPoint2 = None
            self.command.rotateAboutPoint()
            try:
                self.command._f_results_for_caller_and_prepare_for_new_input()
            except AttributeError:
                print_compact_traceback(
                    "bug: command %s has no attr"\
                    "'_f_results_for_caller_and_prepare_for_new_input'.")
                self.command.mouseClickPoints = []
                self.resetVariables()

            self.glpane.gl_update()
            return


        assert len(self.command.mouseClickPoints) <= self.command.mouseClickLimit

        if len(self.command.mouseClickPoints) == self.command.mouseClickLimit:
            self.endPoint2 = None
            self._snapOn = False
            self._standardAxisVectorForDrawingSnapReference = None
            self.glpane.gl_update()
            self.command.rotateAboutPoint()
            #Exit this GM's command (i.e. the command 'RotateAboutPoint')
            self.command.command_Done()
        return

    def _getCursorText_length(self, vec):
        """
        Overrides superclass method.
        @see: self._drawCursorText() for details.
        """
        #Based on Mark's email (as of 2008-12-08) , the rotate about point don't
        #need length in the cursor text. So just return an empty string
        return ''

    def _getCursorText_angle(self, vec):
        """
        Subclasses may override this method.
        @see: self._drawCursorText() for details.
        """
        thetaString = ''

        if len(self.command.mouseClickPoints) < 2:
            theta = self.glpane.get_angle_made_with_screen_right(vec)
            thetaString = "%5.2f deg" % (theta,)
        else:
            ref_vector = norm(self.command.mouseClickPoints[1] - self.pivotPoint)
            quat = Q(vec, ref_vector)
            theta = quat.angle * 180.0 / math.pi
            thetaString = "%5.2f deg" % (theta,)

        return thetaString


    def _getAtomHighlightColor(self, selobj):
        return env.prefs[atomHighlightColor_prefs_key]

    def update_cursor_for_no_MB(self):
        """
        Update the cursor for this mode.
        """
        if self._snapOn:
            if self._snapType == 'HORIZONTAL':
                self.glpane.setCursor(self.win.rotateAboutPointHorizontalSnapCursor)
            elif self._snapType == 'VERTICAL':
                self.glpane.setCursor(self.win.rotateAboutPointVerticalSnapCursor)
        else:
            self.glpane.setCursor(self.win.rotateAboutPointCursor)

    pass # end of class RotateAboutPoint_GraphicsMode

# ==

class RotateAboutPoint_Command(Line_Command):


    GraphicsMode_class = RotateAboutPoint_GraphicsMode

    commandName = 'RotateAboutPoint'
    featurename = "Rotate About Point"
        # (I don't know if this featurename is ever user-visible;
        #  if it is, it's probably wrong -- consider overriding
        #  self.get_featurename() to return the value from the
        #  prior command, if this is used as a temporary command.
        #  The default implementation returns this constant
        #  or (if it's not overridden in subclasses) something
        #  derived from it. [bruce 071227])
    from utilities.constants import CL_REQUEST
    command_level = CL_REQUEST

    def rotateAboutPoint(self):
        """
        Rotates the selected entities along the specified vector, about the
        specified pivot point (pivot point it the starting point of the
        drawn vector.
        """

        if len(self.mouseClickPoints) != self.mouseClickLimit:
            print_compact_stack("Rotate about point bug: mouseclick points != mouseclicklimit: ")
            return


        pivotPoint = self.mouseClickPoints[0]
        ref_vec_endPoint = self.mouseClickPoints[1]
        rot_vec_endPoint = self.mouseClickPoints[2]

        reference_vec = norm(ref_vec_endPoint - pivotPoint)

        lineVector = norm(rot_vec_endPoint - pivotPoint)


        #lineVector = endPoint - startPoint

        quat1 = Q(lineVector, reference_vec)

        #DEBUG Disabled temporarily . will not be used
        if dot(lineVector, reference_vec) < 0:
            theta = math.pi - quat1.angle
        else:
            theta = quat1.angle

        #TEST_DEBUG-- Works fine
        theta = quat1.angle

        rot_axis = cross(lineVector, reference_vec)


        if dot(lineVector, reference_vec) < 0:
            rot_axis = - rot_axis

        cross_prod_1 = norm(cross(reference_vec, rot_axis))
        cross_prod_2 = norm(cross(lineVector, rot_axis))

        if dot(cross_prod_1, cross_prod_2) < 0:
            quat2 = Q(rot_axis, theta)
        else:
            quat2 = Q(rot_axis, - theta)

        movables = self.graphicsMode.getMovablesForLeftDragging()
        self.assy.rotateSpecifiedMovables(
            quat2,
            movables = movables,
            commonCenter = pivotPoint)

        self.glpane.gl_update()
        return

    def _results_for_request_command_caller(self):
        """
        @return: tuple of results to return to whatever "called"
                 self as a "request command"

        [overrides Line_GraphicsMode method]
        @see: Line_Command._f_results_for_caller_and_prepare_for_new_input()
        """
        #bruce 080801 split this out of former restore_gui method (now inherited).

        # note (updated 2008-09-26): superclass Line_Command.command_entered()
        # sets self._results_callback,and superclass command_will_exit()
        #calls it with this method's return value
        return ()

    pass # end of class RotateAboutPoint_Command

# end