Bug 12919 fixed: Rotation on plots was disabled after using menus
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / JoGLView / interaction / DragZoomRotateInteraction.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  */
11 package org.scilab.modules.renderer.JoGLView.interaction;
12
13 import java.awt.Component;
14 import java.awt.event.MouseAdapter;
15 import java.awt.event.MouseEvent;
16 import java.awt.event.MouseListener;
17 import java.awt.event.MouseMotionAdapter;
18 import java.awt.event.MouseMotionListener;
19 import java.awt.event.MouseWheelEvent;
20 import java.awt.event.MouseWheelListener;
21
22 import org.scilab.modules.commons.OS;
23 import org.scilab.modules.graphic_objects.axes.Axes;
24 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
25 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
26 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
27
28 /**
29  * This class manage figure interaction.
30  *
31  * @author Pierre Lando
32  */
33 public class DragZoomRotateInteraction extends FigureInteraction {
34
35     private static final int XY_TRANSLATION_MODIFIER = MouseEvent.BUTTON1_MASK;
36     private static final int Z_TRANSLATION_MODIFIER = MouseEvent.BUTTON1_MASK | MouseEvent.ALT_MASK;
37     private static final int ROTATION_MODIFIER = MouseEvent.BUTTON3_MASK;
38     private static final int MACOSX_ROTATION_MODIFIER = MouseEvent.BUTTON1_MASK | MouseEvent.CTRL_MASK;
39
40     /**
41      * The box size is multiply by this value.
42      */
43     private static final double ZOOM_FACTOR = 1.02;
44
45     private final MouseListener mouseListener;
46     private final MouseWheelListener mouseWheelListener;
47     private final MouseMotionListener mouseMotionListener;
48
49     /**
50      * Last important mouse event.
51      */
52     private MouseEvent previousEvent;
53     private Axes currentAxes;
54
55     /**
56      * Default constructor.
57      * @param drawerVisitor parent drawer visitor.
58      */
59     public DragZoomRotateInteraction(DrawerVisitor drawerVisitor) {
60         super(drawerVisitor);
61         mouseMotionListener = new FigureMouseMotionListener();
62         mouseWheelListener = new FigureMouseWheelListener();
63         mouseListener = new FigureMouseListener();
64     }
65
66     @Override
67     protected void changeEnable(boolean isEnable) {
68         Component component = getDrawerVisitor().getComponent();
69         if (component != null) {
70             if (isEnable) {
71                 component.addMouseListener(mouseListener);
72                 component.addMouseWheelListener(mouseWheelListener);
73             } else {
74                 component.removeMouseListener(mouseListener);
75                 component.removeMouseMotionListener(mouseMotionListener);
76                 component.removeMouseWheelListener(mouseWheelListener);
77             }
78         }
79     }
80
81     /**
82      * This {@see MouseListner} activate the {@see MouseMotionListener} when at least
83      * one button is pressed.
84      * The event is saved in {@see previousEvent}
85      */
86     private class FigureMouseListener extends MouseAdapter implements MouseListener {
87
88         private int pressedButtons = 0;
89
90         @Override
91         public void mousePressed(MouseEvent e) {
92             if (pressedButtons == 0) {
93                 previousEvent = e;
94                 if (currentAxes == null) {
95                     currentAxes = getUnderlyingAxes(e.getPoint());
96                     if (currentAxes != null) {
97                         getDrawerVisitor().getComponent().addMouseMotionListener(mouseMotionListener);
98                     }
99                 }
100             }
101             pressedButtons++;
102         }
103
104         @Override
105         public void mouseReleased(MouseEvent e) {
106             if (pressedButtons > 0) {
107                 pressedButtons--;
108             }
109
110             if (pressedButtons == 0) {
111                 getDrawerVisitor().getComponent().removeMouseMotionListener(mouseMotionListener);
112                 currentAxes = null;
113             }
114         }
115     }
116
117     /**
118      * This {@see MouseWheelListener} manage zoom/un-zoom on the figure.
119      */
120     private class FigureMouseWheelListener implements MouseWheelListener {
121
122         @Override
123         public void mouseWheelMoved(MouseWheelEvent e) {
124             Axes axes = getUnderlyingAxes(e.getPoint());
125             if (axes != null) {
126                 double scale = Math.pow(ZOOM_FACTOR, e.getUnitsToScroll());
127                 Double[] bounds = axes.getDisplayedBounds();
128
129                 double xDelta = (bounds[1] - bounds[0]) / 2;
130                 double xMiddle = (bounds[1] + bounds[0]) / 2;
131                 bounds[0] = xMiddle - xDelta * scale;
132                 bounds[1] = xMiddle + xDelta * scale;
133
134                 double yDelta = (bounds[3] - bounds[2]) / 2;
135                 double yMiddle = (bounds[3] + bounds[2]) / 2;
136                 bounds[2] = yMiddle - yDelta * scale;
137                 bounds[3] = yMiddle + yDelta * scale;
138
139                 double zDelta = (bounds[5] - bounds[4]) / 2;
140                 double zMiddle = (bounds[5] + bounds[4]) / 2;
141                 bounds[4] = zMiddle - zDelta * scale;
142                 bounds[5] = zMiddle + zDelta * scale;
143
144                 Boolean zoomed = tightZoomBounds(axes, bounds);
145                 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_BOX__, bounds);
146                 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_ENABLED__, zoomed);
147             }
148         }
149     }
150
151     /**
152      * This {@see MouseMotionListener} manage rotation and translation on the figure.
153      */
154     private class FigureMouseMotionListener extends MouseMotionAdapter implements MouseMotionListener {
155
156         @Override
157         public void mouseMoved(MouseEvent e) {
158             /*
159              * Mac OS X specific case: the users first presses CTRL and then left-clic.
160              */
161             if (OS.get() == OS.MAC && e.isControlDown() && e.getButton() == 0) {
162                 doRotation(e);
163             }
164         }
165         public void mouseDragged(MouseEvent e) {
166             switch (e.getModifiers()) {
167                 case MACOSX_ROTATION_MODIFIER:
168                     /*
169                      * Mac OS X specific case: the users first left-clic and then presses CTRL
170                      */
171                     if (OS.get() == OS.MAC && e.isControlDown()) {
172                         doRotation(e);
173                         break;
174                     }
175                 case XY_TRANSLATION_MODIFIER:
176                     doXYTranslation(e);
177                     break;
178                 case Z_TRANSLATION_MODIFIER:
179                     doZTranslation(e);
180                     break;
181                 case ROTATION_MODIFIER:
182                     doRotation(e);
183                     break;
184             }
185
186             previousEvent = e;
187         }
188
189         private void doRotation(MouseEvent e) {
190             int dx = e.getX() - previousEvent.getX();
191             int dy = e.getY() - previousEvent.getY();
192
193             if (currentAxes != null) {
194                 Double[] angles = currentAxes.getRotationAngles();
195                 angles[0] -= dy / 4.0;
196                 angles[1] -= Math.signum(Math.sin(Math.toRadians(angles[0]))) * (dx / 4.0);
197                 GraphicController.getController().setProperty(currentAxes.getIdentifier(), GraphicObjectProperties.__GO_ROTATION_ANGLES__, angles);
198             }
199         }
200
201         private void doXYTranslation(MouseEvent e) {
202             int dx = e.getX() - previousEvent.getX();
203             int dy = e.getY() - previousEvent.getY();
204
205             if (currentAxes != null) {
206                 if (currentAxes.getZoomEnabled()) {
207                     Double[] bounds = currentAxes.getDisplayedBounds();
208                     double orientation = Math.signum(Math.cos(Math.toRadians(currentAxes.getRotationAngles()[0])));
209                     double angle = - orientation * Math.toRadians(currentAxes.getRotationAngles()[1]);
210
211                     double xDelta = (bounds[0] - bounds[1]) / 100;
212                     double yDelta = (bounds[2] - bounds[3]) / 100;
213
214                     double rotatedDX = dx * Math.sin(angle) + dy * Math.cos(angle);
215                     double rotatedDY = dx * Math.cos(angle) - dy * Math.sin(angle);
216
217                     bounds[0] += xDelta * rotatedDX * orientation;
218                     bounds[1] += xDelta * rotatedDX * orientation;
219
220                     bounds[2] += yDelta * rotatedDY;
221                     bounds[3] += yDelta * rotatedDY;
222
223                     Boolean zoomed = tightZoomBoxToDataBounds(currentAxes, bounds);
224                     GraphicController.getController().setProperty(currentAxes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_BOX__, bounds);
225                     GraphicController.getController().setProperty(currentAxes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_ENABLED__, zoomed);
226                 }
227             }
228         }
229
230         private void doZTranslation(MouseEvent e) {
231             int dy = e.getY() - previousEvent.getY();
232
233             if (currentAxes != null) {
234                 Double[] bounds = currentAxes.getDisplayedBounds();
235
236                 double zDelta = (bounds[5] - bounds[4]) / 100;
237
238                 bounds[4] += zDelta * dy;
239                 bounds[5] += zDelta * dy;
240
241                 Boolean zoomed = tightZoomBoxToDataBounds(currentAxes, bounds);
242                 GraphicController.getController().setProperty(currentAxes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_BOX__, bounds);
243                 GraphicController.getController().setProperty(currentAxes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_ENABLED__, zoomed);
244             }
245         }
246
247         /**
248          * Tight given bounds to axes data bounds.
249          * Bounds length along axes are conserved.
250          * @param axes the given axes.
251          * @param zoomBounds the zoomBounds.
252          * @return true if actually there is a zoom.
253          */
254         private boolean tightZoomBoxToDataBounds(Axes axes, Double[] zoomBounds) {
255             boolean zoomed = false;
256             Double[] dataBounds = axes.getMaximalDisplayedBounds();
257             for (int i : new int[] {0, 2, 4}) {
258                 if (zoomBounds[i] < dataBounds[i]) {
259                     double delta = dataBounds[i] - zoomBounds[i];
260                     zoomBounds[i] = dataBounds[i]; // zoomBounds[i] += delta;
261                     zoomBounds[i + 1] += delta;
262                 } else {
263                     zoomed = true;
264                 }
265             }
266
267             for (int i : new int[] {1, 3, 5}) {
268                 if (zoomBounds[i] > dataBounds[i]) {
269                     double delta = dataBounds[i] - zoomBounds[i];
270                     zoomBounds[i] = dataBounds[i]; // zoomBounds[i] += delta;
271                     zoomBounds[i - 1] += delta;
272                 } else {
273                     zoomed = true;
274                 }
275             }
276
277             return zoomed;
278         }
279     }
280 }