2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2012 - Pedro Arthur dos S. Souza
4 * Copyright (C) 2012 - Caio Lucas dos S. Souza
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
14 package org.scilab.modules.gui.editor;
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;
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;
27 import org.scilab.modules.graphic_objects.PolylineData;
28 import org.scilab.modules.graphic_objects.SurfaceData;
29 import org.scilab.modules.gui.editor.CommonHandler;
30 import org.scilab.modules.gui.editor.ObjectSearcher;
32 import java.lang.Math;
36 * Given a (x, y) window coord checks
37 * if it is closer or belongs to a polyline.
39 * @author Caio Souza <caioc2bolado@gmail.com>
40 * @author Pedro Souza <bygrandao@gmail.com>
46 public class EntityPicker {
50 private boolean needTransform = false;
51 private Axes curAxes = null;
52 private final Double selectionDelta = 7.0;
55 * Picks a polyline at the given position.
57 * @param figureUid Figure uid to be check.
58 * @param posX Position on x axis in pixels.
59 * @param posY Position on y axis in pixels.
60 * @return Picked polyline uid or null if there isn't any polyline at the given position.
62 public String pick(String figureUid, Integer posX, Integer posY) {
64 Integer[] position = {posX, posY};
65 String axes = AxesHandler.clickedAxes(figureUid, position);
70 curAxes = AxesHandler.getAxesFromUid(axes);
72 double[] pos = {1.0 * posX, 1.0 * posY, 1.0};
73 double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axes, pos);
75 pos[0] += selectionDelta;
76 pos[1] += selectionDelta;
77 double[] c2d2 = CallRenderer.get2dViewFromPixelCoordinates(axes, pos);
79 dx = Math.abs(c2d[0] - c2d2[0]);
80 dy = Math.abs(c2d[1] - c2d2[1]);
82 needTransform = !isInDefaultView(curAxes);
84 /* Checks if the click is outside canvas drawable area*/
85 if (AxesHandler.isZoomBoxEnabled(axes)) {
86 if (!AxesHandler.isInZoomBoxBounds(axes, c2d[0], c2d[1])) {
91 String polylines[] = (new ObjectSearcher()).search(axes, GraphicObjectProperties.__GO_POLYLINE__);
93 if (polylines != null) {
94 for (int i = 0; i < polylines.length; ++i) {
95 if (CommonHandler.isVisible(polylines[i])) {
97 if (CommonHandler.isLineEnabled(polylines[i])) {
98 if (isOverLine(polylines[i], c2d[0], c2d[1]) != -1) {
102 if (CommonHandler.isMarkEnabled(polylines[i])) {
103 if (isOverMark(polylines[i], c2d[0], c2d[1]) != -1) {
115 * Check algorithm linear interpolation for each pair of points.
116 * @param uid Polyline uid to be checked.
117 * @param x position on x axis in view coordinates.
118 * @param y position on y axis in view coordinates.
119 * @return true if x,y belongs or is closest to the polyline.
121 private int isOverLine(String uid, Double x, Double y) {
123 double[] datax = (double[])PolylineData.getDataX(uid);
124 double[] datay = (double[])PolylineData.getDataY(uid);
125 double[] dataz = (double[])PolylineData.getDataZ(uid);
126 int size = datax.length;
128 if (PolylineData.isXShiftSet(uid) != 0) {
129 double[] x_shift = (double[])PolylineData.getShiftX(uid);
130 for (int i = 0; i < size; ++i) {
131 datax[i] += x_shift[i];
135 if (PolylineData.isYShiftSet(uid) != 0) {
136 double[] y_shift = (double[])PolylineData.getShiftY(uid);
137 for (int i = 0; i < size; ++i) {
138 datay[i] += y_shift[i];
142 if (PolylineData.isZShiftSet(uid) != 0) {
143 double[] z_shift = (double[])PolylineData.getShiftZ(uid);
144 for (int i = 0; i < size; ++i) {
145 dataz[i] += z_shift[i];
148 datax = CommonHandler.toLogScale(datax, curAxes.getXAxisLogFlag());
149 datay = CommonHandler.toLogScale(datay, curAxes.getYAxisLogFlag());
150 dataz = CommonHandler.toLogScale(dataz, curAxes.getZAxisLogFlag());
152 double[] oldPoint = null;
154 oldPoint = transformPoint(curAxes, datax[0], datay[0], dataz[0]);
157 for (Integer i = 0; i < (size - 1); ++i) {
159 double[] newPoint = transformPoint(curAxes, datax[i + 1], datay[i + 1], dataz[i + 1]);
160 if (isInRange(oldPoint[0], newPoint[0], oldPoint[1], newPoint[1], x, y)) {
165 if (isInRange(datax[i], datax[i + 1], datay[i], datay[i + 1], x, y)) {
173 private boolean isInRange(Double x0, Double x1, Double y0, Double y1, Double x, Double y) {
174 /* Fast bound check*/
175 double m = (x1 + x0) / 2;
178 double ca = (y1 - y0) / (x1 - x0);
180 double yy = y0 + ca * (x - x0);
182 double pix = this.dx / selectionDelta;
183 double m_y = (y1 + y0) / 2;
184 double dy = m_y - y0;
186 boolean ca_inf = (Math.abs(x1 - x0) < Math.abs(pix * 2));
187 boolean in_bounds = (Math.abs(m - x) <= Math.abs(pix * 2)) && (Math.abs(m_y - y) <= Math.abs(dy));
190 * test if (x, y) belongs or is closer to the line
191 * if the angular coeficent -> inf(ca_inf), the interpolation fails
192 * then we use "in_bunds" test.
194 return (Math.abs(m - x) <= Math.abs(dx)) && (y >= (yy - this.dy)) && (y <= (yy + this.dy)) || (ca_inf && in_bounds);
198 * Checks if the given point belongs the polyline mark.
199 * @param uid Polyline uid to be checked.
200 * @param x position on x axis in view coordinates.
201 * @param y position on y axis in view coordinates.
202 * @return True if x,y belongs to the polyline mark.
204 private int isOverMark(String uid, Double x, Double y) {
206 double[] datax = (double[])PolylineData.getDataX(uid);
207 double[] datay = (double[])PolylineData.getDataY(uid);
208 double[] dataz = (double[])PolylineData.getDataZ(uid);
210 if (PolylineData.isXShiftSet(uid) != 0) {
211 double[] x_shift = (double[])PolylineData.getShiftX(uid);
212 for (int i = 0; i < datax.length; ++i) {
213 datax[i] += x_shift[i];
217 if (PolylineData.isYShiftSet(uid) != 0) {
218 double[] y_shift = (double[])PolylineData.getShiftY(uid);
219 for (int i = 0; i < datay.length; ++i) {
220 datay[i] += y_shift[i];
224 if (PolylineData.isZShiftSet(uid) != 0) {
225 double[] z_shift = (double[])PolylineData.getShiftZ(uid);
226 for (int i = 0; i < dataz.length; ++i) {
227 dataz[i] += z_shift[i];
231 datax = CommonHandler.toLogScale(datax, curAxes.getXAxisLogFlag());
232 datay = CommonHandler.toLogScale(datay, curAxes.getYAxisLogFlag());
233 dataz = CommonHandler.toLogScale(dataz, curAxes.getZAxisLogFlag());
235 Integer size = CommonHandler.getMarkSize(uid);
236 Integer unit = CommonHandler.getMarkSizeUnit(uid);
238 int finalSize = (unit == 1) ? (8 + 2 * size) : size;
240 double deltax = Math.abs((dx / selectionDelta) * finalSize);
241 double deltay = Math.abs((dy / selectionDelta) * finalSize);
244 for (int i = 0; i < datax.length; ++i) {
246 double[] point = transformPoint(curAxes, datax[i], datay[i], dataz[i]);
247 if ((Math.abs(point[0] - x) <= deltax) && (Math.abs(point[1] - y) <= deltay)) {
251 if ((Math.abs(datax[i] - x) <= deltax) && (Math.abs(datay[i] - y) <= deltay)) {
260 public class PickedPoint {
262 public boolean isSegment;
263 PickedPoint(int p, boolean segment) {
270 * Given a polyline uid checks if the given point (px,py)
271 * belongs or is closer to any polyline point.
273 * @return The picked point or PickedPoint.point = -1 otherwise.
275 public PickedPoint pickPoint(String uid, int px, int py) {
277 PickedPoint point = new PickedPoint(-1, false);
278 Integer[] position = {px, py};
279 String figUid = (new ObjectSearcher()).searchParent(uid, GraphicObjectProperties.__GO_FIGURE__);
280 String axes = AxesHandler.clickedAxes(figUid, position);
285 curAxes = AxesHandler.getAxesFromUid(axes);
287 double[] pos = {1.0 * px, 1.0 * py, 1.0};
288 double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axes, pos);
290 pos[0] += selectionDelta;
291 pos[1] += selectionDelta;
292 double[] c2d2 = CallRenderer.get2dViewFromPixelCoordinates(axes, pos);
294 dx = Math.abs(c2d[0] - c2d2[0]);
295 dy = Math.abs(c2d[1] - c2d2[1]);
297 needTransform = !isInDefaultView(curAxes);
300 point.point = isOverMark(uid, c2d[0], c2d[1]);
301 if (point.point != -1) {
304 /*try pick a segment*/
305 point.point = isOverLine(uid, c2d[0], c2d[1]);
306 point.isSegment = true;
312 * Checks if the axes is in default view (2d view).
314 * @return true if is in default view, false otherwise.
316 private boolean isInDefaultView(Axes axes) {
317 Double rot[] = axes.getRotationAngles();
318 return (rot[0] == 0.0 && rot[1] == 270.0);
322 * Project the given point in view plane.
324 * @return the projected point.
326 private double[] transformPoint(Axes axes, double x, double y, double z) {
327 double point[] = {x, y, z};
328 return AxesDrawer.compute2dViewCoordinates(axes, point);
332 public class LegendInfo {
333 public String legend = null;
334 public String polyline = null;
335 LegendInfo(String legend, String polyline) {
336 this.legend = legend;
337 this.polyline = polyline;
342 * Check if the given position is over a legend object.
344 * @param axes The uid of axes that was clicked.
345 * @param position The mouse position (x, y).
346 * @return The LegendInfo if picked a legend null otherwise.
348 public LegendInfo pickLegend(String figure, Integer[] position) {
350 String axes = AxesHandler.clickedAxes(figure, position);
354 String legend = LegendHandler.searchLegend(axes);
355 if (legend == null) {
360 Integer[] axesSize = {0, 0};
362 Double[] axesBounds = { 0., 0. }, dPosition = { 0., 0. }, legendPos = { 0., 0. }, legendBounds = { 0., 0., 0., 0. }, dimension = { 0., 0. };
364 axesSize = (Integer[])GraphicController.getController().getProperty(figure, GraphicObjectProperties.__GO_AXES_SIZE__);
365 axesBounds = (Double[])GraphicController.getController().getProperty(axes, GraphicObjectProperties.__GO_AXES_BOUNDS__);
366 legendPos = (Double[])GraphicController.getController().getProperty(legend, GraphicObjectProperties.__GO_POSITION__);
367 links = (String[])GraphicController.getController().getProperty(legend, GraphicObjectProperties.__GO_LINKS__);
368 dPosition[0] = (position[0] - (axesBounds[0] * axesSize[0])) / (axesBounds[2] * axesSize[0]);
369 dPosition[1] = (position[1] - (axesBounds[1] * axesSize[1])) / (axesBounds[3] * axesSize[1]);
370 dimension = (Double[])GraphicController.getController().getProperty(legend, GraphicObjectProperties.__GO_SIZE__);
371 legendBounds[0] = legendPos[0];
372 legendBounds[1] = legendPos[1];
373 legendBounds[2] = legendPos[0] + dimension[0];
374 legendBounds[3] = legendPos[1] + dimension[1];
375 delta = dimension[1] / (1.0 * links.length);
377 if (dPosition[0] >= legendBounds[0] && dPosition[0] <= legendBounds[2] && dPosition[1] >= legendBounds[1] && dPosition[1] <= legendBounds[3]) {
378 for (Integer i = 0; i < links.length; i++) {
379 if (dPosition[1] >= (legendBounds[1] + i * delta) && dPosition[1] <= (legendBounds[1] + (i + 1) * delta)) {
380 return new LegendInfo(legend, links[links.length - 1 - i]);
388 * Try pick an axis label at the given position.
389 * @param figure The figure uid.
390 * @param pos The position (x, y).
391 * @return The picked axis label or null.
393 public static AxesHandler.axisTo pickLabel(String figure, Integer[] pos) {
395 String axes = AxesHandler.clickedAxes(figure, pos);
400 Double radAngle = 0.;
402 double[] point = new double[3];
404 String[] label = { (String)GraphicController.getController().getProperty(axes, GraphicObjectProperties.__GO_X_AXIS_LABEL__),
405 (String)GraphicController.getController().getProperty(axes, GraphicObjectProperties.__GO_Y_AXIS_LABEL__),
406 (String)GraphicController.getController().getProperty(axes, GraphicObjectProperties.__GO_Z_AXIS_LABEL__),
407 (String)GraphicController.getController().getProperty(axes, GraphicObjectProperties.__GO_TITLE__)
410 boolean[] logScale = { (Boolean)GraphicController.getController().getProperty(axes, GraphicObjectProperties.__GO_X_AXIS_LOG_FLAG__),
411 (Boolean)GraphicController.getController().getProperty(axes, GraphicObjectProperties.__GO_Y_AXIS_LOG_FLAG__)
414 for (Integer i = 0; i < 4; i++) {
415 corners = (Double[])GraphicController.getController().getProperty(label[i], GraphicObjectProperties.__GO_CORNERS__);
416 radAngle = (Double)GraphicController.getController().getProperty(label[i], GraphicObjectProperties.__GO_FONT_ANGLE__);
417 rotate = ((int)((radAngle * 2) / Math.PI)) % 2;
419 corners = rotateCorners(corners);
424 coord = CallRenderer.get2dViewFromPixelCoordinates(axes, point);
426 coord[0] = CommonHandler.InverseLogScale(coord[0], logScale[0]);
427 coord[1] = CommonHandler.InverseLogScale(coord[1], logScale[1]);
429 if ((coord[0] >= corners[0] && coord[0] <= corners[6]) || (coord[0] <= corners[0] && coord[0] >= corners[6])) {
430 if ((coord[1] >= corners[1] && coord[1] <= corners[4]) || (coord[1] <= corners[1] && coord[1] >= corners[4])) {
433 return AxesHandler.axisTo.__X__;
435 return AxesHandler.axisTo.__Y__;
437 return AxesHandler.axisTo.__Z__;
439 return AxesHandler.axisTo.__TITLE__;
448 * Rotate the Corners points 90 degrees
450 * @param vec The corners points vector(should be 4 points x 3 coord, arranged sequentially)
452 private static Double[] rotateCorners(Double[] vec) {
454 Integer length = vec.length;
455 Double[] newVec = new Double[length];
456 for (Integer i = 0; i < length - 3; i++) {
457 newVec[i + 3] = vec[i];
459 newVec[0] = vec[length - 3];
460 newVec[1] = vec[length - 2];
461 newVec[2] = vec[length - 1];
466 * Given a figure search for all plot3d's and for each surface
467 * test if the ray given by the mouse position intersects any surface
468 * @param figure Figure unique identifier.
469 * @param pos Mouse position (x, y).
470 * @return The nearest surface intersected or null otherwise.
472 String pickSurface(String figure, Integer[] pos) {
474 String uid = AxesHandler.clickedAxes(figure, pos);
475 curAxes = (Axes)GraphicController.getController().getObjectFromId(uid);
477 double[] mat = DrawerVisitor.getVisitor(figure).getAxesDrawer().getProjection(uid).getMatrix();
480 Vector3d v0 = AxesDrawer.unProject(curAxes, new Vector3d(1.0f * pos[0], 1.0f * pos[1], 0.0));
481 Vector3d v1 = AxesDrawer.unProject(curAxes, new Vector3d(1.0f * pos[0], 1.0f * pos[1], 1.0));
482 Vector3d Dir = v0.minus(v1).getNormalized();
485 Integer[] types = {GraphicObjectProperties.__GO_PLOT3D__, GraphicObjectProperties.__GO_FAC3D__, GraphicObjectProperties.__GO_GRAYPLOT__};
486 String[] objs = (new ObjectSearcher()).searchMultiple(figure, types);
488 String picked = null;
490 for (int i = 0; i < objs.length; ++i) {
491 double curZ = SurfaceData.pickSurface(objs[i], v0.getX(), v0.getY(), v0.getZ(),
492 Dir.getX(), Dir.getY(), Dir.getZ(), mat[2], mat[6], mat[10], mat[14]);
504 public String pickDatatip(String figure, Integer[] pos) {
506 String axes = AxesHandler.clickedAxes(figure, pos);
511 curAxes = AxesHandler.getAxesFromUid(axes);
512 String[] datatips = (new ObjectSearcher()).search(axes, GraphicObjectProperties.__GO_DATATIP__);
513 boolean[] logFlags = { curAxes.getXAxisLogFlag(), curAxes.getYAxisLogFlag(), curAxes.getZAxisLogFlag()};
515 if (datatips != null) {
517 needTransform = !isInDefaultView(curAxes);
518 double[] pix_pos = {1.0 * pos[0], 1.0 * pos[1], 1.0};
519 double[] c2d = CallRenderer.get2dViewFromPixelCoordinates(axes, pix_pos);
522 double[] c2d2 = CallRenderer.get2dViewFromPixelCoordinates(axes, pix_pos);
523 double dx = Math.abs(c2d[0] - c2d2[0]);
524 double dy = Math.abs(c2d[1] - c2d2[1]);
526 for (int i = 0; i < datatips.length; ++i) {
528 Double[] tip_pos = (Double[])GraphicController.getController().getProperty(datatips[i], GraphicObjectProperties.__GO_DATATIP_DATA__);
529 double point[] = new double[3];
530 point[0] = CommonHandler.logScale(tip_pos[0], logFlags[0]);
531 point[1] = CommonHandler.logScale(tip_pos[1], logFlags[1]);
532 point[2] = CommonHandler.logScale(tip_pos[2], logFlags[2]);
536 point = transformPoint(curAxes, tip_pos[0], tip_pos[1], tip_pos[2]);
539 Integer size = CommonHandler.getMarkSize(datatips[i]);
540 Integer unit = CommonHandler.getMarkSizeUnit(datatips[i]);
542 int finalSize = (unit == 1) ? (8 + 2 * size) : size;
545 if ((Math.abs(point[0] - c2d[0]) <= dx * finalSize) && (Math.abs(point[1] - c2d[1]) <= dy * finalSize)) {