Fix rubberbox coordinates
[scilab.git] / scilab / modules / renderer / src / java / org / scilab / modules / renderer / JoGLView / interaction / RubberBox.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009-2010 - DIGITEO - Pierre Lando
4  * Copyright (C) 2012 - Scilab Enterprises - Bruno JOFRET
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11  */
12
13 package org.scilab.modules.renderer.JoGLView.interaction;
14
15 import java.awt.Component;
16 import java.awt.Point;
17 import java.awt.event.KeyEvent;
18 import java.awt.event.KeyListener;
19 import java.awt.event.MouseEvent;
20 import java.awt.event.MouseListener;
21 import java.awt.event.MouseMotionListener;
22 import java.text.DecimalFormat;
23
24 import javax.swing.event.EventListenerList;
25
26 import org.scilab.forge.scirenderer.DrawingTools;
27 import org.scilab.forge.scirenderer.SciRendererException;
28 import org.scilab.forge.scirenderer.buffers.BuffersManager;
29 import org.scilab.forge.scirenderer.buffers.ElementsBuffer;
30 import org.scilab.forge.scirenderer.buffers.IndicesBuffer;
31 import org.scilab.forge.scirenderer.shapes.appearance.Appearance;
32 import org.scilab.forge.scirenderer.shapes.appearance.Color;
33 import org.scilab.forge.scirenderer.shapes.geometry.DefaultGeometry;
34 import org.scilab.forge.scirenderer.shapes.geometry.Geometry;
35 import org.scilab.forge.scirenderer.tranformations.Vector3d;
36 import org.scilab.modules.graphic_objects.axes.Axes;
37 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
38 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
39 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
40 import org.scilab.modules.renderer.JoGLView.axes.ruler.AxesRulerSpriteFactory;
41 import org.scilab.modules.renderer.JoGLView.interaction.util.HelpersGeometry;
42 import org.scilab.modules.renderer.JoGLView.interaction.util.PointAComputer;
43 import org.scilab.modules.renderer.JoGLView.interaction.util.PointBComputer;
44 import org.scilab.modules.renderer.JoGLView.interaction.util.PointCComputer;
45 import org.scilab.modules.renderer.JoGLView.interaction.util.PointComputer;
46 import org.scilab.modules.renderer.JoGLView.interaction.util.PointDComputer;
47 import org.scilab.modules.renderer.JoGLView.postRendering.PostRendered;
48 import org.scilab.modules.localization.Messages;
49
50 /**
51  * @author Pierre Lando
52  */
53 public class RubberBox extends FigureInteraction implements PostRendered, MouseListener, MouseMotionListener, KeyListener {
54
55     /** Decimal format used to show info messages */
56     private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.###E0");
57
58     static {
59         AxesRulerSpriteFactory.setScilabStyle(DECIMAL_FORMAT);
60     }
61
62     /** Axes name used to show info messages */
63     private static final String[] AXES_NAMES = new String[] {"X", "Y", "Z"};
64
65     /** The cube indices */
66     private static final int[] CUBE_INDICES = {
67         0, 1, 3, 2, 4, 5, 7, 6,
68         0, 3, 1, 2, 4, 7, 5, 6,
69         0, 4, 1, 5, 3, 7, 2, 6
70     };
71
72     /** Rubber box status */
73     public static enum Status {
74         WAIT_POINT_A,
75         WAIT_POINT_B,
76         WAIT_POINT_C,
77         WAIT_POINT_D,
78     }
79
80     /** Rubber box color */
81     private static final Color RUBBER_BOX_COLOR = new Color(.2f, .3f, .4f);
82
83     /** Rubber box thickness */
84     private static final float RUBBER_BOX_THICKNESS = 2;
85
86     /** Rubber box pattern */
87     private static final short RUBBER_BOX_PATTERN = (short) 0xFAFA;
88
89     /** Helpers appearance */
90     private static Appearance helpersAppearance;
91
92     /** Rubber box cube appearance */
93     private static Appearance cubeAppearance;
94
95     /** The event listener list */
96     private final EventListenerList listenerList = new EventListenerList();
97
98     /** Helpers geometry */
99     private HelpersGeometry helpersGeometry;
100
101     /** Rubber box cube geometry */
102     private DefaultGeometry cubeGeometry;
103
104     /** Current status */
105     protected Status status;
106
107     protected Axes axes;
108
109     private PointComputer pointAComputer;
110     protected PointComputer pointBComputer;
111     private PointComputer pointCComputer;
112     private PointComputer pointDComputer;
113     protected Vector3d firstPoint;
114     protected Vector3d secondPoint;
115
116     protected int mouseButton;
117
118     /**
119      * Default constructor.
120      *
121      * @param drawerVisitor parent drawer visitor.
122      */
123     protected RubberBox(DrawerVisitor drawerVisitor) {
124         super(drawerVisitor);
125         axes = drawerVisitor.getAxes();
126         status = Status.WAIT_POINT_A;
127     }
128
129     /**
130      * Add a rubber box listener.
131      * The listener will be notified when a rubber box end.
132      * @param rubberBoxListener the new listener.
133      */
134     public final void addListener(RubberBoxListener rubberBoxListener) {
135         listenerList.add(RubberBoxListener.class, rubberBoxListener);
136     }
137
138     /**
139      * Remove a rubber box listener.
140      * The listener will no long be notified on events.
141      * @param rubberBoxListener the removed listener.
142      */
143     public final void removeListener(RubberBoxListener rubberBoxListener) {
144         listenerList.remove(RubberBoxListener.class, rubberBoxListener);
145     }
146
147     /**
148      * Notify all listener that the rubber box have ended
149      */
150     protected void fireRubberBoxEnd() {
151         for (RubberBoxListener rubberBoxListener : listenerList.getListeners(RubberBoxListener.class)) {
152             rubberBoxListener.rubberBoxEnd();
153         }
154     }
155
156     @Override
157     public final void draw(DrawingTools drawingTools) throws SciRendererException {
158         if (isEnable() && (axes != null)) {
159             drawingTools.getTransformationManager().useSceneCoordinate();
160             drawingTools.getTransformationManager().getModelViewStack().push(
161                 getDrawerVisitor().getAxesDrawer().getSceneProjection(axes.getIdentifier())
162             );
163
164             if (status != Status.WAIT_POINT_A) {
165                 drawingTools.draw(getCubeGeometry(drawingTools), getCubeAppearance());
166             }
167
168             if (secondPoint != null) {
169                 drawingTools.draw(getHelpersGeometry(drawingTools), getHelpersAppearance());
170             }
171
172             drawingTools.getTransformationManager().getModelViewStack().pop();
173         }
174     }
175
176     @Override
177     public final void changeEnable(boolean isEnable) {
178         Component component = getDrawerVisitor().getComponent();
179         if (isEnable) {
180             //status = Status.WAIT_POINT_A;
181             pointAComputer = null;
182             component.addMouseListener(this);
183             component.addMouseMotionListener(this);
184             component.addKeyListener(this);
185             component.setFocusTraversalKeysEnabled(false);
186             component.setFocusable(true);
187             component.requestFocus();
188         } else {
189             component.removeMouseListener(this);
190             component.removeMouseMotionListener(this);
191             component.removeKeyListener(this);
192         }
193         updateInfoMessage();
194         getDrawerVisitor().getCanvas().redraw();
195     }
196
197     @Override
198     public final void keyTyped(KeyEvent e) {
199         if (e.getKeyChar() == KeyEvent.VK_ESCAPE) {
200             mouseButton = -1;
201             setEnable(false);
202             fireRubberBoxEnd();
203         }
204     }
205
206     @Override
207     public void mouseClicked(MouseEvent e) {
208         mouseButton = e.getButton();
209         switch (status) {
210             case WAIT_POINT_A:
211                 if (setPointA(e.getPoint())) {
212                     status = Status.WAIT_POINT_B;
213                 } else {
214                     setEnable(false);
215                     fireRubberBoxEnd();
216                 }
217                 break;
218             case WAIT_POINT_B:
219                 setPointB(e.getPoint());
220                 if (pointBComputer.is2D()) {
221                     process();
222                     setEnable(false);
223                     fireRubberBoxEnd();
224                 } else {
225                     status = Status.WAIT_POINT_C;
226                 }
227                 break;
228             case WAIT_POINT_C:
229                 setPointC(e.getPoint());
230                 status = Status.WAIT_POINT_D;
231                 break;
232             case WAIT_POINT_D:
233                 setPointD(e.getPoint());
234                 process();
235                 setEnable(false);
236                 fireRubberBoxEnd();
237                 break;
238             default:
239         }
240         updateInfoMessage();
241
242
243     }
244
245     @Override
246     public final void mouseMoved(MouseEvent e) {
247         switch (status) {
248             case WAIT_POINT_A:
249                 setPointA(e.getPoint());
250                 getDrawerVisitor().getCanvas().redraw();
251                 break;
252             case WAIT_POINT_B:
253                 setPointB(e.getPoint());
254                 getDrawerVisitor().getCanvas().redraw();
255                 break;
256             case WAIT_POINT_C:
257                 setPointC(e.getPoint());
258                 getDrawerVisitor().getCanvas().redraw();
259                 break;
260             case WAIT_POINT_D:
261                 setPointD(e.getPoint());
262                 getDrawerVisitor().getCanvas().redraw();
263                 break;
264             default:
265         }
266         updateInfoMessage();
267     }
268
269     /**
270      * Update displayed info message.
271      */
272     protected void updateInfoMessage() {
273         if (isEnable()) {
274             switch (status) {
275                 case WAIT_POINT_A:
276                     setInfoMessage(Messages.gettext("Click to set first bounds"), pointAComputer, false);
277                     break;
278                 case WAIT_POINT_B:
279                     setInfoMessage(Messages.gettext("Click to set second bounds"), pointBComputer, false);
280                     break;
281                 case WAIT_POINT_C:
282                     setInfoMessage(Messages.gettext("Click to set first"), pointCComputer, true);
283                     break;
284                 case WAIT_POINT_D:
285                     setInfoMessage(Messages.gettext("Click to set second"), pointDComputer, true);
286                     break;
287                 default:
288             }
289         } else {
290             GraphicController.getController().setProperty(
291                 getDrawerVisitor().getFigure().getIdentifier(),
292                 GraphicObjectProperties.__GO_INFO_MESSAGE__,
293                 ""
294             );
295         }
296     }
297
298     /**
299      * Set the info message.
300      * @param baseMessage the base of the message.
301      * @param pointComputer current used point computer.
302      * @param oneAxis true if only one coordinate is currently set.
303      */
304     private void setInfoMessage(String baseMessage, PointComputer pointComputer, boolean oneAxis) {
305         if ((pointComputer != null) && (pointComputer.isValid())) {
306             String message = baseMessage + " ";
307             double[] data = pointComputer.getSecondPosition().getData();
308             double[][] factors = axes.getScaleTranslateFactors();
309             data[0] = (data[0] - factors[1][0]) / factors[0][0];
310             data[1] = (data[1] - factors[1][1]) / factors[0][1];
311             data[2] = (data[2] - factors[1][2]) / factors[0][2];
312
313             String comma;
314             if (oneAxis) {
315                 comma = "";
316             } else {
317                 comma = ", ";
318             }
319
320             for (int i = 0; i < PointComputer.AXIS_NUMBER; i++) {
321                 if ((i != pointComputer.getFirstAxisIndex()) ^ oneAxis) {
322                     message += AXES_NAMES[i] + " = " + DECIMAL_FORMAT.format(data[i]) + comma;
323                     comma = "";
324                 }
325             }
326             GraphicController.getController().setProperty(
327                 getDrawerVisitor().getFigure().getIdentifier(),
328                 GraphicObjectProperties.__GO_INFO_MESSAGE__,
329                 message
330             );
331         } else {
332             String message = Messages.gettext("Move your mouse on an axes box.");
333             GraphicController.getController().setProperty(
334                 getDrawerVisitor().getFigure().getIdentifier(),
335                 GraphicObjectProperties.__GO_INFO_MESSAGE__,
336                 message
337             );
338         }
339     }
340
341     /**
342      * Process action on RupperBox
343      * Do nothing by default
344      */
345     protected void process() {
346         // Do nothing
347     }
348
349     /**
350      * Set the first point.
351      * @param point first point AWT coordinate.
352      * @return true if the first point is valid.
353      */
354     protected boolean setPointA(Point point) {
355         axes = getUnderlyingAxes(point);
356         if (axes != null) {
357             PointAComputer pointComputer = new PointAComputer(axes, point);
358             if (pointComputer.isValid()) {
359                 this.pointAComputer = pointComputer;
360                 firstPoint = pointComputer.getPosition();
361                 secondPoint = firstPoint;
362                 return true;
363             }
364         }
365         return false;
366     }
367
368     /**
369      * Set second point in 3D zoom.
370      * @param point second point.
371      * @return true if the point is valid.
372      */
373     protected boolean setPointB(Point point) {
374         PointBComputer pointComputer = new PointBComputer(axes, pointAComputer, point);
375         if (pointComputer.isValid()) {
376             this.pointBComputer = pointComputer;
377             firstPoint = pointComputer.getFirstPosition();
378             secondPoint = pointComputer.getSecondPosition();
379             getDrawerVisitor().getCanvas().redraw();
380             return true;
381         } else {
382             return false;
383         }
384     }
385
386     /**
387      * Set zoom box position in 3D zoom.
388      * @param point mouse position.
389      * @return true if the point is valid.
390      */
391     protected boolean setPointC(Point point) {
392         PointCComputer pointComputer = new PointCComputer(axes, pointBComputer, point);
393         if (pointComputer.isValid()) {
394             this.pointCComputer = pointComputer;
395             firstPoint = pointComputer.getFirstPosition();
396             secondPoint = pointComputer.getSecondPosition();
397             getDrawerVisitor().getCanvas().redraw();
398             return true;
399         } else {
400             return false;
401         }
402     }
403
404     /**
405      * Set zoom box position in 3D zoom.
406      * @param point mouse position.
407      * @return true if the point is valid.
408      */
409     protected boolean setPointD(Point point) {
410         PointDComputer pointComputer = new PointDComputer(axes, pointCComputer, point);
411         if (pointComputer.isValid()) {
412             this.pointDComputer = pointComputer;
413             firstPoint = pointComputer.getFirstPosition();
414             secondPoint = pointComputer.getSecondPosition();
415             getDrawerVisitor().getCanvas().redraw();
416             return true;
417         } else {
418             return false;
419         }
420     }
421
422     /**
423      * Initialise or update the helpers geometry.
424      * @param drawingTools the drawing tools used to draw the helpers.
425      * @return updated helpers geometry.
426      */
427     private Geometry getHelpersGeometry(DrawingTools drawingTools) {
428         if (helpersGeometry == null) {
429             helpersGeometry = new HelpersGeometry(drawingTools);
430         }
431         helpersGeometry.updateVertex(axes, pointAComputer, secondPoint, status);
432         return helpersGeometry;
433     }
434
435     /**
436      * Helpers appearance getter.
437      * First initialise the helpers appearance.
438      * @return the helpers appearance.
439      */
440     public final Appearance getHelpersAppearance() {
441         if (helpersAppearance == null) {
442             helpersAppearance = new Appearance();
443             helpersAppearance.setLineColor(new Color(1, 0, 0));
444             helpersAppearance.setLineWidth(2);
445         }
446         return helpersAppearance;
447     }
448
449     /**
450      * Rubber box cube geometry getter.
451      * @param drawingTools the drawing tools.
452      * @return the rubber box cubeGeometry.
453      */
454     private Geometry getCubeGeometry(DrawingTools drawingTools) {
455         if (cubeGeometry == null) {
456             cubeGeometry = new DefaultGeometry();
457
458             BuffersManager bufferManager = drawingTools.getCanvas().getBuffersManager();
459             ElementsBuffer vertexBuffer = bufferManager.createElementsBuffer();
460             IndicesBuffer indicesBuffer = bufferManager.createIndicesBuffer();
461             indicesBuffer.setData(CUBE_INDICES);
462
463             cubeGeometry.setLineDrawingMode(Geometry.LineDrawingMode.SEGMENTS);
464             cubeGeometry.setFillDrawingMode(Geometry.FillDrawingMode.NONE);
465             cubeGeometry.setVertices(vertexBuffer);
466             cubeGeometry.setWireIndices(indicesBuffer);
467         }
468
469         cubeGeometry.getVertices().setData(new float[] {
470                                                (float) firstPoint.getX(), (float) firstPoint.getY(), (float) firstPoint.getZ(), 1,
471                                                (float) firstPoint.getX(), (float) firstPoint.getY(), (float) secondPoint.getZ(), 1,
472                                                (float) firstPoint.getX(), (float) secondPoint.getY(), (float) secondPoint.getZ(), 1,
473                                                (float) firstPoint.getX(), (float) secondPoint.getY(), (float) firstPoint.getZ(), 1,
474                                                (float) secondPoint.getX(), (float) firstPoint.getY(), (float) firstPoint.getZ(), 1,
475                                                (float) secondPoint.getX(), (float) firstPoint.getY(), (float) secondPoint.getZ(), 1,
476                                                (float) secondPoint.getX(), (float) secondPoint.getY(), (float) secondPoint.getZ(), 1,
477                                                (float) secondPoint.getX(), (float) secondPoint.getY(), (float) firstPoint.getZ(), 1
478                                            }, 4);
479
480         return cubeGeometry;
481     }
482
483     /**
484      * Rubber-box cube appearance getter.
485      * @return the rubber-box cube appearance.
486      */
487     private Appearance getCubeAppearance() {
488         if (cubeAppearance == null) {
489             cubeAppearance = new Appearance();
490             cubeAppearance.setLineColor(RUBBER_BOX_COLOR);
491             cubeAppearance.setLineWidth(RUBBER_BOX_THICKNESS);
492             cubeAppearance.setLinePattern(RUBBER_BOX_PATTERN);
493         }
494         return cubeAppearance;
495     }
496
497     @Override
498     public void mousePressed(MouseEvent e) {
499     }
500
501     @Override
502     public void mouseReleased(MouseEvent e) {
503     }
504
505     @Override
506     public void mouseEntered(MouseEvent e) {
507     }
508
509     @Override
510     public void mouseExited(MouseEvent e) {
511     }
512
513     @Override
514     public void mouseDragged(MouseEvent e) {
515     }
516
517     @Override
518     public void keyPressed(KeyEvent e) {
519     }
520
521     @Override
522     public void keyReleased(KeyEvent e) {
523     }
524     
525     public double[] getResults() {
526         double[][] factors = axes.getScaleTranslateFactors();
527         double result[] = {
528             mouseButton - 1,
529             (Math.min(firstPoint.getX(), secondPoint.getX()) - factors[1][0]) / factors[0][0],
530             (Math.max(firstPoint.getY(), secondPoint.getY()) - factors[1][1]) / factors[0][1],
531             (Math.max(firstPoint.getZ(), secondPoint.getZ()) - factors[1][2]) / factors[0][2],
532             (Math.abs(firstPoint.getX() - secondPoint.getX())) / factors[0][0],
533             (Math.abs(firstPoint.getY() - secondPoint.getY())) / factors[0][1],
534             (Math.abs(firstPoint.getZ() - secondPoint.getZ())) / factors[0][2]
535         };
536
537         return result;
538     }
539 }