2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando
4 * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET
6 * Copyright (C) 2012 - 2016 - Scilab Enterprises
8 * This file is hereby licensed under the terms of the GNU GPL v2.0,
9 * pursuant to article 5.3.4 of the CeCILL v.2.1.
10 * This file was originally licensed under the terms of the CeCILL v2.1,
11 * and continues to be available under such terms.
12 * For more information, see the COPYING file which you should have received
13 * along with this program.
15 package org.scilab.modules.renderer.JoGLView.interaction;
17 import java.awt.Component;
18 import java.awt.Cursor;
19 import java.awt.event.MouseAdapter;
20 import java.awt.event.MouseEvent;
21 import java.awt.event.MouseListener;
22 import java.awt.event.MouseMotionAdapter;
23 import java.awt.event.MouseMotionListener;
24 import java.awt.event.MouseWheelEvent;
25 import java.awt.event.MouseWheelListener;
27 import org.scilab.modules.commons.OS;
28 import org.scilab.modules.graphic_objects.axes.Axes;
29 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
30 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
31 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
32 import org.scilab.modules.renderer.JoGLView.util.ScaleUtils;
36 * This class manage figure interaction.
38 * @author Pierre Lando
40 public class DragZoomRotateInteraction extends FigureInteraction {
42 private static final int XY_TRANSLATION_MODIFIER = MouseEvent.BUTTON1_MASK;
43 private static final int Z_TRANSLATION_MODIFIER = MouseEvent.BUTTON1_MASK | MouseEvent.ALT_MASK;
44 private static final int ROTATION_MODIFIER = MouseEvent.BUTTON3_MASK;
45 private static final int MACOSX_ROTATION_MODIFIER = MouseEvent.BUTTON1_MASK | MouseEvent.CTRL_MASK;
48 * The box size is multiply by this value.
50 private static final double ZOOM_FACTOR = 1.02;
52 private final MouseListener mouseListener;
53 private final MouseWheelListener mouseWheelListener;
54 private final MouseMotionListener mouseMotionListener;
57 * Last important mouse event.
59 private MouseEvent previousEvent;
60 private Axes[] currentAxes;
64 * Default constructor.
65 * @param drawerVisitor parent drawer visitor.
67 public DragZoomRotateInteraction(DrawerVisitor drawerVisitor) {
69 mouseMotionListener = new FigureMouseMotionListener();
70 mouseWheelListener = new FigureMouseWheelListener();
71 mouseListener = new FigureMouseListener();
72 currentAxes = new Axes[0];
76 protected void changeEnable(boolean isEnable) {
77 Component component = getDrawerVisitor().getComponent();
78 if (component != null) {
80 component.addMouseListener(mouseListener);
81 component.addMouseWheelListener(mouseWheelListener);
83 component.removeMouseListener(mouseListener);
84 component.removeMouseMotionListener(mouseMotionListener);
85 component.removeMouseWheelListener(mouseWheelListener);
90 public void setTranslationEnable(boolean status) {
91 ((FigureMouseMotionListener)mouseMotionListener).setTranslateEnable(status);
95 * This {@see MouseListner} activate the {@see MouseMotionListener} when at least
96 * one button is pressed.
97 * The event is saved in {@see previousEvent}
99 private class FigureMouseListener extends MouseAdapter implements MouseListener {
101 private int pressedButtons = 0;
104 public void mousePressed(MouseEvent e) {
105 if (pressedButtons == 0) {
107 if (currentAxes.length == 0) {
108 currentAxes = getAllUnderlyingAxes(e.getPoint());
109 if (currentAxes.length > 0) {
110 getDrawerVisitor().getComponent().addMouseMotionListener(mouseMotionListener);
111 switch (e.getButton()) {
112 case MouseEvent.BUTTON1 :
113 Cursor cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
114 e.getComponent().setCursor(cursor);
116 case MouseEvent.BUTTON3 :
117 // FIXME: add rotation cursor here
127 public void mouseReleased(MouseEvent e) {
128 if (pressedButtons > 0) {
132 if (pressedButtons == 0) {
133 getDrawerVisitor().getComponent().removeMouseMotionListener(mouseMotionListener);
134 currentAxes = new Axes[0];
136 e.getComponent().setCursor(Cursor.getDefaultCursor());
141 * This {@see MouseWheelListener} manage zoom/un-zoom on the figure.
143 private class FigureMouseWheelListener implements MouseWheelListener {
145 private void applyZoom(Axes axes, double scale) {
147 Double[] bounds = axes.getDisplayedBounds();
148 double[][] factors = axes.getScaleTranslateFactors();
150 double xDelta = (bounds[1] - bounds[0]) / 2;
151 double xMiddle = (bounds[1] + bounds[0]) / 2;
152 bounds[0] = xMiddle - xDelta * scale;
153 bounds[1] = xMiddle + xDelta * scale;
155 double yDelta = (bounds[3] - bounds[2]) / 2;
156 double yMiddle = (bounds[3] + bounds[2]) / 2;
157 bounds[2] = yMiddle - yDelta * scale;
158 bounds[3] = yMiddle + yDelta * scale;
160 double zDelta = (bounds[5] - bounds[4]) / 2;
161 double zMiddle = (bounds[5] + bounds[4]) / 2;
162 bounds[4] = zMiddle - zDelta * scale;
163 bounds[5] = zMiddle + zDelta * scale;
165 bounds[0] = bounds[0] * factors[0][0] + factors[1][0];
166 bounds[1] = bounds[1] * factors[0][0] + factors[1][0];
167 bounds[2] = bounds[2] * factors[0][1] + factors[1][1];
168 bounds[3] = bounds[3] * factors[0][1] + factors[1][1];
169 bounds[4] = bounds[4] * factors[0][2] + factors[1][2];
170 bounds[5] = bounds[5] * factors[0][2] + factors[1][2];
172 Boolean zoomed = tightZoomBounds(axes, bounds);
174 bounds[0] = (bounds[0] - factors[1][0]) / factors[0][0];
175 bounds[1] = (bounds[1] - factors[1][0]) / factors[0][0];
176 bounds[2] = (bounds[2] - factors[1][1]) / factors[0][1];
177 bounds[3] = (bounds[3] - factors[1][1]) / factors[0][1];
178 bounds[4] = (bounds[4] - factors[1][2]) / factors[0][2];
179 bounds[5] = (bounds[5] - factors[1][2]) / factors[0][2];
181 boolean[] logFlags = { axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
182 ScaleUtils.applyInverseLogScaleToBounds(bounds, logFlags);
184 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_BOX__, bounds);
185 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_ENABLED__, zoomed);
189 public void mouseWheelMoved(MouseWheelEvent e) {
191 if (e.isControlDown()) {
192 allAxes = getAllVisibleAxes(e.getPoint());
194 allAxes = getAllUnderlyingAxes(e.getPoint());
196 double scale = Math.pow(ZOOM_FACTOR, e.getUnitsToScroll());
197 for (Axes axes : allAxes) {
198 applyZoom(axes, scale);
203 private static void applyUnlog(Double[] bounds, Axes axes) {
204 if (axes.getXAxisLogFlag()) {
205 bounds[0] = Math.pow(10, bounds[0]);
206 bounds[1] = Math.pow(10, bounds[1]);
209 if (axes.getYAxisLogFlag()) {
210 bounds[2] = Math.pow(10, bounds[2]);
211 bounds[3] = Math.pow(10, bounds[3]);
214 if (axes.getZAxisLogFlag()) {
215 bounds[4] = Math.pow(10, bounds[4]);
216 bounds[5] = Math.pow(10, bounds[5]);
221 * This {@see MouseMotionListener} manage rotation and translation on the figure.
223 private class FigureMouseMotionListener extends MouseMotionAdapter implements MouseMotionListener {
225 private boolean translateEnabled = true;
227 public void setTranslateEnable(boolean status) {
228 translateEnabled = status;
232 public void mouseMoved(MouseEvent e) {
234 * Mac OS X specific case: the users first presses CTRL and then left-clic.
236 if (OS.get() == OS.MAC && e.isControlDown() && e.getButton() == 0) {
240 public void mouseDragged(MouseEvent e) {
241 switch (e.getModifiers()) {
242 case MACOSX_ROTATION_MODIFIER:
244 * Mac OS X specific case: the users first left-clic and then presses CTRL
246 if (OS.get() == OS.MAC && e.isControlDown()) {
251 case XY_TRANSLATION_MODIFIER:
252 if (translateEnabled) {
256 case Z_TRANSLATION_MODIFIER:
259 case ROTATION_MODIFIER:
266 private void doRotation(MouseEvent e) {
267 int dx = e.getX() - previousEvent.getX();
268 int dy = e.getY() - previousEvent.getY();
270 for (Axes axes : currentAxes) {
271 if (axes.getView() != 0) {
272 Double[] angles = axes.getRotationAngles();
273 angles[0] -= dy / 4.0;
274 angles[1] -= Math.signum(Math.sin(Math.toRadians(angles[0]))) * (dx / 4.0);
275 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ROTATION_ANGLES__, angles);
280 private void doXYTranslation(MouseEvent e) {
281 int dx = e.getX() - previousEvent.getX();
282 int dy = e.getY() - previousEvent.getY();
284 for (Axes axes : currentAxes) {
285 if (axes.getZoomEnabled()) {
286 Double[] bounds = axes.getDisplayedBounds();
288 Integer[] winSize = (Integer[]) GraphicController.getController().getProperty(axes.getParent(), GraphicObjectProperties.__GO_AXES_SIZE__);
289 if (winSize == null) {
291 Double[] position = (Double[]) GraphicController.getController().getProperty(axes.getParent(), GraphicObjectProperties.__GO_POSITION__);
292 winSize = new Integer[2];
293 winSize[0] = position[2].intValue();
294 winSize[1] = position[3].intValue();
296 Double[] axesBounds = (Double[]) GraphicController.getController().getProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_AXES_BOUNDS__);
297 Double[] axesMargins = (Double[]) GraphicController.getController().getProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_MARGINS__);
298 Integer view = (Integer) GraphicController.getController().getProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_VIEW__);
300 // Compute ratio from pixel move to user displayed data bounds
301 double xDelta = Math.abs(bounds[0] - bounds[1]) / (winSize[0] * axesBounds[2] * (1 - axesMargins[0] - axesMargins[1]));
302 double yDelta = Math.abs(bounds[2] - bounds[3]) / (winSize[1] * axesBounds[3] * (1 - axesMargins[2] - axesMargins[3]));
306 bounds[0] -= xDelta * dx;
307 bounds[1] -= xDelta * dx;
309 bounds[2] += yDelta * dy;
310 bounds[3] += yDelta * dy;
313 double orientation = - Math.signum(Math.cos(Math.toRadians(axes.getRotationAngles()[0])));
314 double angle = - orientation * Math.toRadians(axes.getRotationAngles()[1]);
316 double rotatedDX = dx * Math.sin(angle) + dy * Math.cos(angle);
317 double rotatedDY = dx * Math.cos(angle) - dy * Math.sin(angle);
319 bounds[0] -= xDelta * rotatedDX * orientation;
320 bounds[1] -= xDelta * rotatedDX * orientation;
322 bounds[2] += yDelta * rotatedDY;
323 bounds[3] += yDelta * rotatedDY;
326 Boolean zoomed = tightZoomBoxToDataBounds(axes, bounds);
327 boolean[] logFlags = { axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
328 ScaleUtils.applyInverseLogScaleToBounds(bounds, logFlags);
330 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_BOX__, bounds);
331 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_ENABLED__, zoomed);
336 private void doZTranslation(MouseEvent e) {
337 int dy = e.getY() - previousEvent.getY();
339 for (Axes axes : currentAxes) {
340 Double[] bounds = axes.getDisplayedBounds();
342 double zDelta = (bounds[5] - bounds[4]) / 100;
344 bounds[4] += zDelta * dy;
345 bounds[5] += zDelta * dy;
347 Boolean zoomed = tightZoomBoxToDataBounds(axes, bounds);
348 boolean[] logFlags = { axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
349 ScaleUtils.applyInverseLogScaleToBounds(bounds, logFlags);
351 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_BOX__, bounds);
352 GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_ENABLED__, zoomed);
357 * Tight given bounds to axes data bounds.
358 * Bounds length along axes are conserved.
359 * @param axes the given axes.
360 * @param zoomBounds the zoomBounds.
361 * @return true if actually there is a zoom.
363 private boolean tightZoomBoxToDataBounds(Axes axes, Double[] zoomBounds) {
364 boolean zoomed = false;
365 Double[] dataBounds = axes.getMaximalDisplayedBounds();
366 for (int i : new int[] {0, 2, 4}) {
367 if (zoomBounds[i] < dataBounds[i]) {
368 double delta = dataBounds[i] - zoomBounds[i];
369 zoomBounds[i] = dataBounds[i]; // zoomBounds[i] += delta;
370 zoomBounds[i + 1] += delta;
376 for (int i : new int[] {1, 3, 5}) {
377 if (zoomBounds[i] > dataBounds[i]) {
378 double delta = dataBounds[i] - zoomBounds[i];
379 zoomBounds[i] = dataBounds[i]; // zoomBounds[i] += delta;
380 zoomBounds[i - 1] += delta;