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