[renderer] mouseWheel zoom now centered on pointer location
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / editor / GEDPicker.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2013 - Pedro Arthur dos S. Souza
4  * Copyright (C) 2013 - Caio Lucas dos S. Souza
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
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.
14  *
15  */
16
17 package org.scilab.modules.gui.editor;
18
19
20 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
21 import org.scilab.modules.graphic_objects.graphicObject.GraphicObject;
22 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
23 import org.scilab.modules.graphic_objects.CallGraphicController;
24 import org.scilab.modules.renderer.CallRenderer;
25
26 import org.scilab.modules.graphic_objects.axes.Axes;
27 import org.scilab.modules.renderer.JoGLView.axes.AxesDrawer;
28 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
29 import org.scilab.forge.scirenderer.tranformations.Vector3d;
30
31 import org.scilab.modules.graphic_objects.PolylineData;
32 import org.scilab.modules.graphic_objects.SurfaceData;
33 import org.scilab.modules.graphic_objects.ObjectData;
34 import org.scilab.modules.renderer.utils.CommonHandler;
35 import org.scilab.modules.renderer.utils.AxesHandler;
36
37 import java.lang.Math;
38 import java.util.List;
39 import java.util.LinkedList;
40 /**
41  *
42  * GED Object picker for all Graphics Objects
43  *
44  */
45 public class GEDPicker {
46
47     private final Double delta = 7.0;
48     private Integer axesUID;
49     private Axes axes;
50     private double Z = 2;
51
52     /**
53      * Picker, given a figure and a click position in pixel coordinates
54      * it returns an array of objects picked
55      *
56      * @param figureUID Unique id from the figure object
57      * @param position Mouse click position in pixels
58      * @return Array of picked objects
59      */
60     public Integer[] pick(Integer figureUID, Integer[] position) {
61
62         Integer[] AllObjs;
63         boolean curObj = false;
64
65         axesUID = AxesHandler.clickedAxes(figureUID, position);
66         axes = AxesHandler.getAxesFromUid(axesUID);
67
68         if (axes == null) {
69             return new Integer[] { figureUID };
70         }
71         List<Integer> ret = new LinkedList<Integer>();
72         AllObjs = getAllObjAsArray(axesUID);
73
74         for (int i = 0; i < AllObjs.length; i++) {
75
76             Integer type = (Integer)GraphicController.getController().getProperty(AllObjs[i], GraphicObjectProperties.__GO_TYPE__);
77             if (!CommonHandler.isVisible(AllObjs[i])) {
78                 continue;
79             }
80             switch (type) {
81
82                 case GraphicObjectProperties.__GO_POLYLINE__:
83                     curObj = getPolyline(AllObjs[i], position);
84                     break;
85                 case GraphicObjectProperties.__GO_PLOT3D__:
86                 case GraphicObjectProperties.__GO_GRAYPLOT__:
87                 case GraphicObjectProperties.__GO_FAC3D__:
88                 case GraphicObjectProperties.__GO_MATPLOT__:
89                     curObj = getSurface(AllObjs[i], position);
90                     break;
91                 case GraphicObjectProperties.__GO_LEGEND__:
92                     curObj = getLegend(AllObjs[i], position);
93                     break;
94                 case GraphicObjectProperties.__GO_LABEL__:
95                     curObj = getLabel(AllObjs[i], position);
96                     break;
97                 case GraphicObjectProperties.__GO_ARC__:
98                     curObj = getArc(AllObjs[i], position);
99                     break;
100                 case GraphicObjectProperties.__GO_CHAMP__:
101                     curObj = getChamp(AllObjs[i], position);
102                     break;
103                 case GraphicObjectProperties.__GO_FEC__:
104                     curObj = getFec(AllObjs[i], position);
105                     break;
106                 case GraphicObjectProperties.__GO_RECTANGLE__:
107                     curObj = getRectangle(AllObjs[i], position);
108                     break;
109                 case GraphicObjectProperties.__GO_SEGS__:
110                     curObj = getSegs(AllObjs[i], position);
111                     break;
112                 case GraphicObjectProperties.__GO_DATATIP__:
113                     curObj = getDatatip(AllObjs[i], position);
114                     break;
115                 case GraphicObjectProperties.__GO_COMPOUND__:
116                     curObj = false;
117                     break;
118             }
119             if (curObj) {
120                 ret.add(AllObjs[i]);
121             }
122         }
123         if (ret.size() > 0) {
124             return ret.toArray(new Integer[ret.size()]);
125         }
126
127         if (isInsideAxes(figureUID, position)) {
128             return new Integer[] { axesUID };
129         } else {
130             return new Integer[] { figureUID };
131         }
132     }
133
134     /**
135      * Polyline picker, given a polyline object it checks if the click
136      * was over it
137      *
138      * @param obj The given polyline object uid
139      * @param position Mouse click position in pixels
140      * @return true if picked the polyline otherwise returns false
141      */
142     boolean getPolyline(Integer obj, Integer[] position) {
143
144         double[] datax = (double[])PolylineData.getDataX(obj);
145         double[] datay = (double[])PolylineData.getDataY(obj);
146         double[] dataz = (double[])PolylineData.getDataZ(obj);
147
148         if (PolylineData.isXShiftSet(obj) != 0) {
149             double[] x_shift = (double[])PolylineData.getShiftX(obj);
150             for (int i = 0; i < datax.length; ++i) {
151                 datax[i] += x_shift[i];
152             }
153         }
154
155         if (PolylineData.isYShiftSet(obj) != 0) {
156             double[] y_shift = (double[])PolylineData.getShiftY(obj);
157             for (int i = 0; i < datay.length; ++i) {
158                 datay[i] += y_shift[i];
159             }
160         }
161
162         if (PolylineData.isZShiftSet(obj) != 0) {
163             double[] z_shift = (double[])PolylineData.getShiftZ(obj);
164             for (int i = 0; i < dataz.length; ++i) {
165                 dataz[i] += z_shift[i];
166             }
167         }
168
169         datax = CommonHandler.toLogScale(datax, axes.getXAxisLogFlag());
170         datay = CommonHandler.toLogScale(datay, axes.getYAxisLogFlag());
171         dataz = CommonHandler.toLogScale(dataz, axes.getZAxisLogFlag());
172
173
174         double[] pos = {1.0 * position[0], 1.0 * position[1], 1.0};
175         double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
176
177         pos[0] += delta;
178         pos[1] += delta;
179         double[] c2d2 = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
180
181         double dx = Math.abs(c2d[0] - c2d2[0]);
182         double dy = Math.abs(c2d[1] - c2d2[1]);
183
184         if (AxesHandler.isZoomBoxEnabled(axesUID)) {
185             if (!AxesHandler.isInZoomBoxBounds(axesUID, c2d[0], c2d[1])) {
186                 return false;
187             }
188         }
189
190         Double[] rotAngles = (Double[])GraphicController.getController().getProperty(axesUID, GraphicObjectProperties.__GO_ROTATION_ANGLES__);
191         boolean default2dView = (rotAngles[0] == 0.0 && rotAngles[1] == 270.0);
192         double[] oldPoint = new double[3];
193
194         if (CommonHandler.isLineEnabled(obj)) {
195             //System.out.println("lineEnable");
196             if (!default2dView) {
197                 oldPoint = AxesDrawer.compute2dViewCoordinates(axes, new double[] { datax[0], datay[0], dataz[0] });
198                 //System.out.println("NotDefault");
199             }
200
201             for (Integer i = 0; i < (datax.length - 1); ++i) {
202                 if (!default2dView) {
203                     double[] newPoint = AxesDrawer.compute2dViewCoordinates(axes, new double[] {datax[i + 1], datay[i + 1], dataz[i + 1]});
204                     if (isInRange(oldPoint[0], newPoint[0], oldPoint[1], newPoint[1], c2d[0], c2d[1], dx, dy)) {
205                         //System.out.println("in");
206                         return true;
207                     }
208                     oldPoint = newPoint;
209                 } else {
210                     if (isInRange(datax[i], datax[i + 1], datay[i], datay[i + 1], c2d[0], c2d[1], dx, dy)) {
211                         return true;
212                     }
213                 }
214             }
215         }
216
217         if (CommonHandler.isMarkEnabled(obj)) {
218
219             Integer markSize = CommonHandler.getMarkSize(obj);
220             Integer unit = CommonHandler.getMarkSizeUnit(obj);
221
222             int finalSize = (unit == 1) ? (8 + 2 * markSize) : markSize;
223             finalSize /= 2;
224             double deltax = Math.abs((dx / delta) * finalSize);
225             double deltay = Math.abs((dy / delta) * finalSize);
226
227
228             for (int i = 0; i < datax.length; ++i) {
229                 if (!default2dView) {
230                     double[] point = AxesDrawer.compute2dViewCoordinates(axes, new double[] {datax[i], datay[i], dataz[i]});
231                     if ((Math.abs(point[0] - c2d[0]) <= deltax) && (Math.abs(point[1] - c2d[1]) <= deltay)) {
232                         return true;
233                     }
234                 } else {
235                     if ((Math.abs(datax[i] - c2d[0]) <= deltax) && (Math.abs(datay[i] - c2d[1]) <= deltay)) {
236                         return true;
237                     }
238                 }
239             }
240         }
241
242         return false;
243     }
244
245     /**
246      * Surface picker, given a plot3d/fac3d/grayplot/matplot object it checks if the click
247      * was over it
248      *
249      * @param obj The given surface object uid
250      * @param position Mouse click position in pixels
251      * @return true if picked the surface otherwise returns false
252      */
253     boolean getSurface(Integer obj, Integer[] position) {
254         GraphicObject go = GraphicController.getController().getObjectFromId(obj);
255         Integer figure = go.getParentFrameOrFigure();
256         double[] mat = DrawerVisitor.getVisitor(figure).getAxesDrawer().getProjection(axesUID).getMatrix();
257         double[][] factors = axes.getScaleTranslateFactors();
258
259         Vector3d v0 = AxesDrawer.unProject(axes, new Vector3d(1.0f * position[0], 1.0f * position[1], 0.0));
260         Vector3d v1 = AxesDrawer.unProject(axes, new Vector3d(1.0f * position[0], 1.0f * position[1], 1.0));
261         v0 = new Vector3d((v0.getX() - factors[1][0]) / factors[0][0], (v0.getY() - factors[1][1]) / factors[0][1], (v0.getZ() - factors[1][2]) / factors[0][2]);
262         v1 = new Vector3d((v1.getX() - factors[1][0]) / factors[0][0], (v1.getY() - factors[1][1]) / factors[0][1], (v1.getZ() - factors[1][2]) / factors[0][2]);
263
264         Vector3d Dir = v0.minus(v1).getNormalized();
265         // ray is the parametric cuve t -> v0 + t * Dir
266
267         double t = SurfaceData.pickSurface(obj, v0.getX(), v0.getY(), v0.getZ(), Dir.getX(), Dir.getY(), Dir.getZ());
268
269         if (t !=  Double.NEGATIVE_INFINITY) {
270             Vector3d intersectPoint = new Vector3d(v0.plus(Dir.times(t)));
271             double curZ = intersectPoint.getX() * mat[2] + intersectPoint.getY() * mat[6] + intersectPoint.getZ() * mat[10] + mat[14];
272             if (curZ < Z) {
273                 return true;
274             }
275         }
276         return false;
277     }
278
279     /**
280      * Legend picker, given a legend object it checks if the click
281      * was over it
282      *
283      * @param obj The given legend object uid
284      * @param position Mouse click position in pixels
285      * @return true if picked the legend otherwise returns false
286      */
287     boolean getLegend(Integer obj, Integer[] position) {
288         Integer[] axesSize = {0, 0};
289         Double delta;
290         Double[] axesBounds = { 0., 0. }, dPosition = { 0., 0. }, legendPos = { 0., 0. }, legendBounds = { 0., 0., 0., 0. }, dimension = { 0., 0. };
291
292         GraphicObject go = GraphicController.getController().getObjectFromId(obj);
293         Integer figure = go.getParentFrameOrFigure();
294         axesSize = (Integer[])GraphicController.getController().getProperty(figure, GraphicObjectProperties.__GO_AXES_SIZE__);
295         axesBounds = (Double[])GraphicController.getController().getProperty(axesUID, GraphicObjectProperties.__GO_AXES_BOUNDS__);
296         legendPos = (Double[])GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_POSITION__);
297         dPosition[0] = (position[0] - (axesBounds[0] * axesSize[0])) / (axesBounds[2] * axesSize[0]);
298         dPosition[1] = (position[1] - (axesBounds[1] * axesSize[1])) / (axesBounds[3] * axesSize[1]);
299         dimension = (Double[])GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_SIZE__);
300         legendBounds[0] =  legendPos[0];
301         legendBounds[1] = legendPos[1];
302         legendBounds[2] = legendPos[0] + dimension[0];
303         legendBounds[3] = legendPos[1] + dimension[1];
304
305         if (dPosition[0] >= legendBounds[0] && dPosition[0] <= legendBounds[2] && dPosition[1] >= legendBounds[1] && dPosition[1] <= legendBounds[3]) {
306             return true;
307         }
308         return false;
309     }
310
311     /**
312      * Label picker, given a label object it checks if the click
313      * was over it
314      *
315      * @param obj The given surface object uid
316      * @param position Mouse click position in pixels
317      * @return true if picked the surface otherwise returns false
318      */
319     boolean getLabel(Integer obj, Integer[] position) {
320
321         Double[] corners = (Double[])GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_CORNERS__);
322         Double radAngle = (Double)GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_FONT_ANGLE__);
323         int rotate = ((int)((radAngle * 2) / Math.PI)) % 2;
324         if (rotate == 1) {
325
326             Double[] temp = new Double[corners.length];
327             for (Integer i = 0; i < corners.length - 3; i++) {
328                 temp[i + 3] = corners[i];
329             }
330             temp[0] = corners[corners.length - 3];
331             temp[1] = corners[corners.length - 2];
332             temp[2] = corners[corners.length - 1];
333             corners = temp;
334         }
335         double[] point = new double[] { 1.0 * position[0], 1.0 * position[1], 1.0};
336         double[] coord = CallRenderer.get2dViewFromPixelCoordinates(axesUID, point);
337         boolean[] logScale = {  (Boolean)GraphicController.getController().getProperty(axesUID, GraphicObjectProperties.__GO_X_AXIS_LOG_FLAG__),
338                                 (Boolean)GraphicController.getController().getProperty(axesUID, GraphicObjectProperties.__GO_Y_AXIS_LOG_FLAG__)
339                              };
340         coord[0] = CommonHandler.InverseLogScale(coord[0], logScale[0]);
341         coord[1] = CommonHandler.InverseLogScale(coord[1], logScale[1]);
342
343         if ((coord[0] >= corners[0] && coord[0] <= corners[6]) || (coord[0] <= corners[0] && coord[0] >= corners[6])) {
344             if ((coord[1] >= corners[1] && coord[1] <= corners[4]) || (coord[1] <= corners[1] && coord[1] >= corners[4])) {
345                 return true;
346             }
347         }
348         return false;
349     }
350
351     /**
352     * Arc picker, given a arc object it checks if the click
353     * was over it
354     *
355     * @param obj The given arc object uid
356     * @param position Mouse click position in pixels
357     * @return true if picked the arc otherwise returns false
358     */
359     boolean getArc(Integer obj, Integer[] position) {
360
361         double[] upperLeft = (double[])ObjectData.getArcUpperLeftPoint(obj);
362         double[] data = (double[])ObjectData.getArcData(obj);
363         double[] pos = { position[0] * 1.0, position[1] * 1.0, 0.0 };
364         double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
365         pos[0] += delta;
366         pos[1] += delta;
367         double[] c2d2 = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
368         Boolean fill_mode = (Boolean)GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_FILL_MODE__);
369
370         //Calculates the selection delta based on ellipse & axes size
371         double dt = Math.sqrt(Math.pow(c2d[0] - c2d2[0], 2.0) + Math.pow(c2d[1] - c2d2[1], 2.0)) / Math.sqrt((data[0] * data[0] / 4.) / 2. + (data[1] * data[1] / 4.) / 2.);
372
373         double[] c3d1 = AxesDrawer.compute3dViewCoordinates(axes, c2d);
374         c2d[2] += 1.0;
375         double[] c3d2 = AxesDrawer.compute3dViewCoordinates(axes, c2d);
376
377         //Calculates the intersection of the click ray with the ellipse plane
378         Vector3d v0 = new Vector3d(c3d1);
379         Vector3d v1 = new Vector3d(c3d2);
380         Vector3d dir = v1.minus(v0);
381
382         if (dir.getZ() == 0) {
383             return false;
384         }
385
386         double u = (upperLeft[2] - v0.getZ()) / dir.getZ();
387         Vector3d point = v0.plus(dir.times(u));
388
389         double xr = data[1] / 2.;
390         double yr = data[0] / 2.;
391
392         Vector3d center =  new Vector3d(upperLeft[0] + xr, upperLeft[1] - yr, upperLeft[2]);
393
394         //checks if the point lies within the ellipse
395         double x = point.getX() - center.getX();
396         double y = point.getY() - center.getY();
397         double v = (x * x) / (xr * xr) + (y * y) / (yr * yr);
398
399         if (v > 1.0 + dt) {
400             return false;
401         }
402
403         double angle0 = getAngle360(data[2]);
404         double angle1 = getAngle360(data[3]) + angle0;
405
406         /**
407          * Checks if the point is closer to the drawn arc
408          * and inside de given angles. If fill_mode = false
409          * it it accept 1-dt < value < 1+dt, if fill_mode = true
410          * it accept value < 1+dt;
411          */
412
413         v0 = new Vector3d(1.0, 0.0, 0.0);
414         v1 = point.minus(center);
415         double angle = Math.acos(v0.scalar(v1) / (v0.getNorm() * v1.getNorm()));
416         angle += v1.getY() < 0.0 ? Math.PI : 0.0;
417
418         if (!(angle0 == angle1) && !(angle >= angle0 && angle <= angle1)) {
419             return false;
420         }
421
422         if (fill_mode) {
423             return true;
424         } else if (v > 1.0 - dt) {
425             return true;
426         }
427
428         return false;
429     }
430
431     double getAngle360(double a) {
432         while (a > 2 * Math.PI) {
433             a -= 2 * Math.PI;
434         }
435         return a;
436     }
437
438     /**
439      * Champ picker, given a champ object it checks if the click
440      * was over it
441      *
442      * @param obj The given champ object uid
443      * @param position Mouse click position in pixels
444      * @return true if picked the champ otherwise returns false
445      */
446     boolean getChamp(Integer obj, Integer[] position) {
447
448         double[] datax = (double[])ObjectData.getChampX(obj);
449         double[] datay = (double[])ObjectData.getChampY(obj);
450         double[] data = (double[])ObjectData.getArrows(obj);
451
452         double scale = 0;
453         for (int i = 0; i < data.length; i++) {
454             scale = data[i] > scale ? data[i] : scale;
455         }
456
457         double[][] arrows = getAsStdMatrix(data, scale);
458         if (arrows == null) {
459             return false;
460         }
461         double[] pos = {1.0 * position[0], 1.0 * position[1], 1.0};
462
463         double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
464         pos[0] += delta;
465         pos[1] += delta;
466         double[] c2d2 = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
467
468         double dx = Math.abs(c2d[0] - c2d2[0]);
469         double dy = Math.abs(c2d[1] - c2d2[1]);
470         int xSize = datax.length;
471         int ySize = datay.length;
472
473         for (int i = 0; i < xSize; i++) {
474             for (int j = 0; j < ySize; j++) {
475
476                 double[] ch2d = AxesDrawer.compute2dViewCoordinates(axes, new double[] { datax[i], datay[j], 0.0});
477                 double[] temp = { datax[i] + arrows[i * xSize + j][0], datay[j] + arrows[i * xSize + j][1], arrows[i * xSize + j][2]};
478                 double[] ch2d2 = AxesDrawer.compute2dViewCoordinates(axes, temp);
479                 if (isInRange(ch2d[0], ch2d2[0], ch2d[1], ch2d2[1], c2d[0], c2d[1], dx, dy)) {
480                     return true;
481                 }
482                 if ((Math.abs(c2d[0] - ch2d[0]) <= dx) && (Math.abs(c2d[1] - ch2d[1]) <= dy)) {
483                     return true;
484                 }
485                 if ((Math.abs(c2d[0] - ch2d2[0]) <= dx) && (Math.abs(c2d[1] - ch2d2[1]) <= dy)) {
486                     return true;
487                 }
488             }
489         }
490         return false;
491     }
492
493     /**
494      * Fec picker, given a fec object it checks if the click
495      * was over it
496      *
497      * @param obj The given fec object uid
498      * @param position Mouse click position in pixels
499      * @return true if picked the fec otherwise returns false
500      */
501     boolean getFec(Integer obj, Integer[] position) {
502         int numVerticesByElem = ObjectData.getFecNumVerticesByElement(obj);
503         double[] elements = (double[])ObjectData.getFecElements(obj);
504         double[] data = (double[])ObjectData.getFecData(obj);
505
506         double[] pos = { position[0] * 1.0, position[1] * 1.0, 0.0 };
507         double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
508         double[] c3d1 = AxesDrawer.compute3dViewCoordinates(axes, c2d);
509         c2d[2] += 1.0;
510
511         double[] c3d2 = AxesDrawer.compute3dViewCoordinates(axes, c2d);
512         Vector3d l0 = new Vector3d(c3d1);
513         Vector3d l1 = new Vector3d(c3d2);
514         Vector3d dir = l1.minus(l0);
515         int idx1, idx2, idx3;
516         Vector3d p1, p2, p3;
517
518         int tSize = elements.length / (numVerticesByElem + 2);
519         for (int i = 0; i < tSize; i++) {
520             idx1 = (int)elements[tSize + i];
521             for (int j = 2; j < numVerticesByElem; ++j) {
522                 idx2 = (int)elements[j * tSize + i];
523                 idx3 = (int)elements[(j + 1) * tSize + i];
524
525                 p1 = new Vector3d(data[(idx1 - 1) * 3], data[(idx1 - 1) * 3 + 1], data[(idx1 - 1) * 3 + 2]);
526                 p2 = new Vector3d(data[(idx2 - 1) * 3], data[(idx2 - 1) * 3 + 1], data[(idx2 - 1) * 3 + 2]);
527                 p3 = new Vector3d(data[(idx3 - 1) * 3], data[(idx3 - 1) * 3 + 1], data[(idx3 - 1) * 3 + 2]);
528
529                 if (testTri(p1, p2, p3, l0, dir)) {
530                     return true;
531                 }
532             }
533         }
534         return false;
535     }
536     /**
537      * Möller–Trumbore intersection algorithm
538      * Test if a line intersect a triangle
539      *
540      * @param p1 The vertex 1 of the triangle
541      * @param p2 The vertex 2 of the triangle
542      * @param p3 The vertex 3 of the triangle
543      * @param l0 origin point
544      * @param direction The direction vector
545      * @return true if it intersect the triangle, false otherwise
546      */
547     private boolean testTri(Vector3d p1, Vector3d p2, Vector3d p3, Vector3d l0, Vector3d direction) {
548
549         Vector3d e1 = p2.minus(p1);
550         Vector3d e2 = p3.minus(p1);
551
552         Vector3d h = Vector3d.product(direction, e2);
553         double det = e1.scalar(h);
554
555         if (det > -0.00001 && det < 0.00001) {
556             return false;
557         }
558
559         double inv = 1.0 / det;
560         Vector3d s = l0.minus(p1);
561
562         double u = s.scalar(h) * inv;
563         if (u < 0.0 || u > 1.0) {
564             return false;
565         }
566
567         Vector3d q = Vector3d.product(s, e1);
568         double v = direction.scalar(q) * inv;
569         if (v < 0.0 || (u + v) > 1.0) {
570             return false;
571         }
572
573         return true;
574     }
575
576     /**
577      * Rectangle picker, given a rectangle object it checks if the click
578      * was over it
579      *
580      * @param obj The given rectangle object uid
581      * @param position Mouse click position in pixels
582      * @return true if picked the rectangle otherwise returns false
583      */
584     boolean getRectangle(Integer obj, Integer[] position) {
585
586         Double[] upperLeft = (Double[])GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_UPPER_LEFT_POINT__);
587         Double height = (Double)GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_HEIGHT__);
588         Double width = (Double)GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_WIDTH__);
589         double[] pos = { position[0] * 1.0, position[1] * 1.0, 0.0 };
590         double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
591
592         double[] c3d1 = AxesDrawer.compute3dViewCoordinates(axes, c2d);
593         c2d[2] += 1.0;
594         double[] c3d2 = AxesDrawer.compute3dViewCoordinates(axes, c2d);
595
596         Vector3d v0 = new Vector3d(c3d1);
597         Vector3d v1 = new Vector3d(c3d2);
598         Vector3d c = v1.minus(v0);
599
600         if (c.getZ() == 0) {
601             return false;
602         }
603
604         double u = (upperLeft[2] - v0.getZ()) / c.getZ();
605         Vector3d point = v0.plus(c.times(u));
606
607         if (point.getX() >= upperLeft[0] && point.getX() <= (upperLeft[0] + width)
608                 && point.getY() <= upperLeft[1] && point.getY() >= (upperLeft[1] - height)) {
609             return true;
610         }
611         return false;
612     }
613
614     /**
615      * Segs picker, given a segs object it checks if the click
616      * was over it
617      *
618      * @param obj The given segs object uid
619      * @param position Mouse click position in pixels
620      * @return true if picked the segs otherwise returns false
621      */
622     boolean getSegs(Integer obj, Integer[] position) {
623
624         double[][] base = getAsStdMatrix((double[])ObjectData.getSegsData(obj), 1.);
625         double[][] arrows = getAsStdMatrix((double[])ObjectData.getArrows(obj), 1.);
626
627         double[] pos = {1.0 * position[0], 1.0 * position[1], 1.0};
628         double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
629         pos[0] += delta;
630         pos[1] += delta;
631         double[] c2d2 = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pos);
632
633         double dx = Math.abs(c2d[0] - c2d2[0]);
634         double dy = Math.abs(c2d[1] - c2d2[1]);
635         //System.out.println("Click: " + c2d[0] + "," + c2d[1]);
636         for (int i = 0; i < base.length; i++) {
637             double[] ch2d = AxesDrawer.compute2dViewCoordinates(axes, base[i]);
638             double[] ch2d2 = AxesDrawer.compute2dViewCoordinates(axes, arrows[i]);
639             //System.out.println("P1: " + ch2d[0] + "," + ch2d[1]);
640             //System.out.println("P2: " + ch2d2[0] + "," + ch2d2[1]);
641             if (isInRange(ch2d[0], ch2d2[0], ch2d[1], ch2d2[1], c2d[0], c2d[1], dx, dy)) {
642                 return true;
643             }
644         }
645         return false;
646     }
647
648     /**
649      * Datatip picker, given a datatip object it checks if the click
650      * was over it
651      *
652      * @param obj The given datatip object uid
653      * @param position Mouse click position in pixels
654      * @return true if picked the datatip otherwise returns false
655      */
656     boolean getDatatip(Integer obj, Integer[] position) {
657
658         boolean[] logFlags = { axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
659         double[] pix_pos = {1.0 * position[0], 1.0 * position[1], 1.0};
660         double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pix_pos);
661         pix_pos[0] += 1.0;
662         pix_pos[1] += 1.0;
663         double[] c2d2 = CallRenderer.get2dViewFromPixelCoordinates(axesUID, pix_pos);
664         double dx = Math.abs(c2d[0] - c2d2[0]);
665         double dy = Math.abs(c2d[1] - c2d2[1]);
666
667         Double[] tip_pos = (Double[])GraphicController.getController().getProperty(obj, GraphicObjectProperties.__GO_DATATIP_DATA__);
668         double point[] = new double[3];
669         point[0] = CommonHandler.logScale(tip_pos[0], logFlags[0]);
670         point[1] = CommonHandler.logScale(tip_pos[1], logFlags[1]);
671         point[2] = CommonHandler.logScale(tip_pos[2], logFlags[2]);
672
673         Double[] rotAngles = (Double[])GraphicController.getController().getProperty(axesUID, GraphicObjectProperties.__GO_ROTATION_ANGLES__);
674         boolean default2dView = (rotAngles[0] == 0.0 && rotAngles[1] == 270.0);
675
676         if (!default2dView) {
677             point = AxesDrawer.compute2dViewCoordinates(axes, point);
678         }
679
680         Integer size = CommonHandler.getMarkSize(obj);
681         Integer unit = CommonHandler.getMarkSizeUnit(obj);
682         int finalSize = (unit == 1) ? (8 + 2 * size) : size;
683         finalSize /= 2;
684
685         if ((Math.abs(point[0] - c2d[0]) <= dx * finalSize) && (Math.abs(point[1] - c2d[1]) <= dy * finalSize)) {
686             return true;
687         }
688
689         //TODO: Add selection when the click is inside datatip textbox
690
691         return false;
692     }
693
694     boolean isInsideAxes(Integer figureUID, Integer[] position) {
695         Double[] rotAngles = (Double[])GraphicController.getController().getProperty(axesUID, GraphicObjectProperties.__GO_ROTATION_ANGLES__);
696         boolean default2dView = (rotAngles[0] == 0.0 && rotAngles[1] == 270.0);
697         //        if (default2dView) {
698         Double[] dataBounds = (Double[])GraphicController.getController().getProperty(axesUID, GraphicObjectProperties.__GO_DATA_BOUNDS__);
699         double[] c2d = AxesDrawer.compute2dViewFromPixelCoordinates(axes, new double[] { 1.0 * position[0], 1.0 * position[1], 0.0 });
700         if (c2d[0] >= dataBounds[0] && c2d[0] <= dataBounds[1] && c2d[1] >= dataBounds[2] && c2d[1] <= dataBounds[3]) {
701             return true;
702         }
703         //      } else {
704         //TODO: Add check when it is in 3d view
705         //      }
706         return false;
707     }
708
709
710     void getObjects(Integer root, List<Integer> putObjs) {
711
712         Integer count = (Integer)GraphicController.getController().getProperty(root, GraphicObjectProperties.__GO_CHILDREN_COUNT__);
713         if (count > 0) {
714             Integer[] children = (Integer[])GraphicController.getController().getProperty(root, GraphicObjectProperties.__GO_CHILDREN__);
715             for (int i = 0; i < count; i++) {
716                 putObjs.add(children[i]);
717                 getObjects(children[i], putObjs);
718             }
719         }
720     }
721
722     /**
723      * Given an root object return an array with all children objects
724      *
725      * @param root The root object uid
726      * @return Array with all objects
727      */
728     Integer[] getAllObjAsArray(Integer root) {
729
730         List<Integer> objs = new LinkedList<Integer>();
731         getObjects(root, objs);
732         return objs.toArray(new Integer[objs.size()]);
733     }
734
735     private boolean isInRange(Double x0, Double x1, Double y0, Double y1, Double x, Double y, Double xRange, Double yRange) {
736         /* Fast bound check*/
737         double m = (x1 + x0) / 2;
738         double dx = m - x0;
739
740         double ca = (y1 - y0) / (x1 - x0);
741
742         double yy = y0 + ca * (x - x0);
743
744         double pix = xRange / delta;
745         double m_y = (y1 + y0) / 2;
746         double dy = m_y - y0;
747
748         boolean ca_inf = (Math.abs(x1 - x0) < Math.abs(pix * 2));
749         boolean in_bounds = (Math.abs(m - x) <= Math.abs(pix * 2)) && (Math.abs(m_y - y) <= Math.abs(dy));
750
751         /*
752          * test if (x, y) belongs or is closer to the line
753          * if the angular coeficent -> inf(ca_inf), the interpolation fails
754          * then we use "in_bunds" test.
755          */
756         return (Math.abs(m - x) <= Math.abs(dx)) && (y >= (yy - yRange)) && (y <= (yy + yRange)) || (ca_inf && in_bounds);
757     }
758
759     private double[][] getAsStdMatrix(double[] data, double scale) {
760
761         double[][] ret = null;
762         if (data.length % 3 != 0) {
763             return ret;
764         }
765         ret = new double[data.length / 3][3];
766         for (int i = 0; i < data.length / 3; i++) {
767             for (int j = 0; j < 3; j++) {
768                 ret[i][j] = data[i * 3 + j] / scale;
769             }
770         }
771         return ret;
772     }
773 }