[renderer] mouseWheel zoom now centered on pointer location 81/20581/14
St├ęphane Mottelet [Wed, 24 Oct 2018 06:30:19 +0000 (08:30 +0200)]
http://bugzilla.scilab.org/show_bug.cgi?id=12711
video:
http://bugzilla.scilab.org/attachment.cgi?id=4829
http://bugzilla.scilab.org/attachment.cgi?id=4831
http://bugzilla.scilab.org/attachment.cgi?id=4833

Change-Id: I826e3da4489b39d6da466fbf482d11cdc77f2100

scilab/CHANGES.md
scilab/modules/graphic_objects/src/cpp/pickSurface.cpp
scilab/modules/graphic_objects/src/java/org/scilab/modules/graphic_objects/SurfaceData.java
scilab/modules/graphic_objects/src/java/org/scilab/modules/graphic_objects/SurfaceDataJNI.java
scilab/modules/graphic_objects/src/jni/SurfaceData.i
scilab/modules/gui/src/java/org/scilab/modules/gui/editor/Editor.java
scilab/modules/gui/src/java/org/scilab/modules/gui/editor/GEDPicker.java
scilab/modules/renderer/src/java/org/scilab/modules/renderer/JoGLView/interaction/DragZoomRotateInteraction.java
scilab/modules/renderer/src/java/org/scilab/modules/renderer/utils/EntityPicker.java

index d3e3794..155c115 100644 (file)
@@ -454,6 +454,7 @@ Known issues
 * [#12566](http://bugzilla.scilab.org/show_bug.cgi?id=12566): `disp()` of a T-list with a single field set to an encoded integer value generated an error.
 * [#12618](http://bugzilla.scilab.org/show_bug.cgi?id=12618): `mfile2sci()` failed converting `a=1i // ab` into `a=1*%i // ab`.
 * [#12659](http://bugzilla.scilab.org/show_bug.cgi?id=12659): Crash on big modulo.
+* [#12711](http://bugzilla.scilab.org/show_bug.cgi?id=12711): Zoom with mouseWheel was always centered at the center of viewing box and was not using cursor position.
 * [#12810](http://bugzilla.scilab.org/show_bug.cgi?id=12810): support added to build RTL help pages.
 * [#12926](http://bugzilla.scilab.org/show_bug.cgi?id=12926): `~%f==1` was parsed as `~(%f==1)` instead of `(~%f)==1`.
 * [#13306](http://bugzilla.scilab.org/show_bug.cgi?id=13306): Evaluating a function set as a structure field could fail.
index 1fea424..dd44bb5 100644 (file)
@@ -22,8 +22,9 @@ extern "C"
 
 #include "getGraphicObjectProperty.h"
 #include "graphicObjectProperties.h"
+#include "sciprint.h"
 
-    double pickSurface(int uid, double x, double y,  double z, double dx, double dy, double dz, double mx, double my, double mz, double mw);
+double pickSurface(int uid, double x, double y,  double z, double dx, double dy, double dz);
 
 }
 
@@ -96,17 +97,16 @@ public:
 };
 
 int test_tri(Vec3 V1, Vec3 V2, Vec3 V3, Vec3 Dir, Vec3 P0, Vec3 &ret);
-void QuadTestAndSaveZ(double *bounds, Vec3 P0, Vec3 P1, Vec3 P2, Vec3 P3, Vec3 direction, Vec3 point,
-                      double mx, double my, double mz, double mw, double &retZ);
+void QuadTestAndSaveT(double *bounds, Vec3 P0, Vec3 P1, Vec3 P2, Vec3 P3, Vec3 direction, Vec3 point, double &retT);
 
 /*
- * Given a ray (point(x, y,z) + direction(dx, dy, dz))
+ * Given a ray (point(x, y,z) and direction(dx, dy, dz))
  * check if the ray intersects any triangle from the given surface.
- * returns the projected Z from the intersection point (p) (p.x*mx + p.y*my + p.z*mz + mw;)
- * or 2.0 if there isn't intersection (projected z vary between -1.0 - 1.0).
+ * returns t such that the intersection point is (x, y,z) + t * (dx, dy, dz)
+ * or -INFINITY if there isn't intersection.
  */
 
-double pickSurface(int uid, double x, double y,  double z, double dx, double dy, double dz, double mx, double my, double mz, double mw)
+double pickSurface(int uid, double x, double y,  double z, double dx, double dy, double dz)
 {
     double* X = NULL;
     double* Y = NULL;
@@ -114,7 +114,7 @@ double pickSurface(int uid, double x, double y,  double z, double dx, double dy,
 
     int type;
     int * pType = &type;
-    double lastZ = 2.0;
+    double lastT = -INFINITY;
 
     Vec3 direction = Vec3(dx, dy, dz);
     Vec3 point = Vec3(x, y, z);
@@ -169,7 +169,7 @@ double pickSurface(int uid, double x, double y,  double z, double dx, double dy,
                 Vec3 P2 = Vec3(X[i + 1], Y[j + 1], Z[(i + 1) + (j + 1) * numX]);
                 Vec3 P3 = Vec3(X[i],   Y[j + 1], Z[i + (j + 1) * numX]);
 
-                QuadTestAndSaveZ(bounds, P0, P1, P2, P3, direction, point, mx, my, mz, mw, lastZ);
+                QuadTestAndSaveT(bounds, P0, P1, P2, P3, direction, point, lastT);
             }
         }
     }
@@ -201,7 +201,7 @@ double pickSurface(int uid, double x, double y,  double z, double dx, double dy,
             Vec3 P2 = Vec3(X[i + 2], Y[i + 2], Z[i + 2]);
             Vec3 P3 = Vec3(X[i + 3], Y[i + 3], Z[i + 3]);
 
-            QuadTestAndSaveZ(bounds, P0, P1, P2, P3, direction, point, mx, my, mz, mw, lastZ);
+            QuadTestAndSaveT(bounds, P0, P1, P2, P3, direction, point, lastT);
         }
     }
     if (type == __GO_GRAYPLOT__)
@@ -224,7 +224,7 @@ double pickSurface(int uid, double x, double y,  double z, double dx, double dy,
         Vec3 P2 = Vec3(X[numX - 1], Y[numY - 1], 0);
         Vec3 P3 = Vec3(X[0],      Y[numY - 1], 0);
 
-        QuadTestAndSaveZ(bounds, P0, P1, P2, P3, direction, point, mx, my, mz, mw, lastZ);
+        QuadTestAndSaveT(bounds, P0, P1, P2, P3, direction, point, lastT);
     }
     if (type == __GO_MATPLOT__)
     {
@@ -254,9 +254,9 @@ double pickSurface(int uid, double x, double y,  double z, double dx, double dy,
         Vec3 P2 = Vec3(mbounds[2],      mbounds[3],      zShift);
         Vec3 P3 = Vec3(mbounds[0],      mbounds[3],      zShift);
 
-        QuadTestAndSaveZ(bounds, P0, P1, P2, P3, direction, point, mx, my, mz, mw, lastZ);
+        QuadTestAndSaveT(bounds, P0, P1, P2, P3, direction, point, lastT);
     }
-    return lastZ;
+    return lastT;
 
 }
 
@@ -268,10 +268,10 @@ double pickSurface(int uid, double x, double y,  double z, double dx, double dy,
  * 0 <= u <= 1 && 0 <= v <= 1 && (u + v) <= 1, then the
  * intersection point is inside the triangle.
  */
-int test_tri(Vec3 V1, Vec3 V2, Vec3 V3, Vec3 Dir, Vec3 P0, Vec3 &ret)
+int test_tri(Vec3 V1, Vec3 V2, Vec3 V3, Vec3 Dir, Vec3 P0, double &t)
 {
     Vec3 Edge1, Edge2, tVec, pVec, qVec;
-    double det, inv_det, t, u, v;
+    double det, inv_det, u, v;
 
     Edge1 = V2 - V1;
     Edge2 = V3 - V1;
@@ -303,8 +303,6 @@ int test_tri(Vec3 V1, Vec3 V2, Vec3 V3, Vec3 Dir, Vec3 P0, Vec3 &ret)
     }
 
     t = Edge2.dot(qVec) * inv_det;
-    ret = P0 + Dir * t;
-
 
     return 1;
 }
@@ -316,31 +314,28 @@ bool isInViewBox(double * bounds, Vec3 point)
             bounds[4] <= point.z && bounds[5] >= point.z);
 }
 
-void QuadTestAndSaveZ(double *bounds, Vec3 P0, Vec3 P1, Vec3 P2, Vec3 P3, Vec3 direction, Vec3 point,
-                      double mx, double my, double mz, double mw, double &retZ)
+void QuadTestAndSaveT(double *bounds, Vec3 P0, Vec3 P1, Vec3 P2, Vec3 P3, Vec3 direction, Vec3 point, double &retT)
 {
-    Vec3 ret;
-
+    Vec3 intersectionPoint;
+    double t;
     /*test first triangle*/
-    if (test_tri(P0, P1, P2, direction, point, ret) == 1)
+    if (test_tri(P0, P1, P2, direction, point, t) == 1)
     {
         /*the intersection point can be outside the view box(invisible)*/
-        if (isInViewBox(bounds, ret))
+        intersectionPoint = point + direction * t;        
+        if (isInViewBox(bounds, intersectionPoint))
         {
-            /* ray intersects the triangle, then we project only the Z cordinate
-             * and store the nearest projected Z.
-             */
-            double curZ = ret.x * mx + ret.y * my + ret.z * mz + mw;
-            retZ = retZ < curZ ? retZ : curZ;
+            // ray intersects the triangle, we store the greatest t (nearest from viewpoint) 
+            retT = retT > t ? retT : t;
         }
     }
     /*test second triangle*/
-    if (test_tri(P0, P2, P3, direction, point, ret) == 1)
+    if (test_tri(P0, P2, P3, direction, point, t) == 1)
     {
-        if (isInViewBox(bounds, ret))
+        intersectionPoint = point + direction * t;        
+        if (isInViewBox(bounds, intersectionPoint))
         {
-            double curZ = ret.x * mx + ret.y * my + ret.z * mz + mw;
-            retZ = retZ < curZ ? retZ : curZ;
+            retT = retT > t ? retT : t;
         }
     }
 }
index 3266c92..1e731ea 100644 (file)
@@ -19,8 +19,8 @@ public class SurfaceData {
     return SurfaceDataJNI.createObject3dData(obj, newObj, type);
   }
 
-  public static double pickSurface(int uid, double x, double y, double z, double dx, double dy, double dz, double mx, double my, double mz, double mw) {
-    return SurfaceDataJNI.pickSurface(uid, x, y, z, dx, dy, dz, mx, my, mz, mw);
+  public static double pickSurface(int uid, double x, double y, double z, double dx, double dy, double dz) {
+    return SurfaceDataJNI.pickSurface(uid, x, y, z, dx, dy, dz);
   }
 
 }
index d9a30af..fc519d6 100644 (file)
@@ -23,5 +23,5 @@ public class SurfaceDataJNI {
   public final static native Object getSurfDataY(int jarg1);
   public final static native Object getSurfDataZ(int jarg1);
   public final static native int createObject3dData(int jarg1, int jarg2, int jarg3);
-  public final static native double pickSurface(int jarg1, double jarg2, double jarg3, double jarg4, double jarg5, double jarg6, double jarg7, double jarg8, double jarg9, double jarg10, double jarg11);
+  public final static native double pickSurface(int jarg1, double jarg2, double jarg3, double jarg4, double jarg5, double jarg6, double jarg7);
 }
index ed08060..345fe9a 100644 (file)
@@ -175,7 +175,7 @@ int getDataSizeZ(int uid)
 
 %{
 extern int createObject3dData(int obj, int newObj, int type);
-extern double pickSurface(int uid, double x, double y,  double z, double dx, double dy, double dz, double mx, double my, double mz, double mw);
+extern double pickSurface(int uid, double x, double y,  double z, double dx, double dy, double dz);
 %}
 
 
@@ -183,7 +183,7 @@ double * getSurfDataX(int uid);
 double * getSurfDataY(int uid);
 double * getSurfDataZ(int uid);
 int createObject3dData(int obj, int newObj, int type);
-double pickSurface(int uid, double x, double y,  double z, double dx, double dy, double dz, double mx, double my, double mz, double mw);
+double pickSurface(int uid, double x, double y,  double z, double dx, double dy, double dz);
 
 
 
index 4afc200..d80d83a 100644 (file)
@@ -72,6 +72,7 @@ public class Editor {
     JMenu labels, legends;
 
     EntityPicker.LegendInfo selectedLegend = null;
+    EntityPicker.SurfaceInfo selectedSurface = null;
     Integer selected = null;
     Integer figureUid = null;
     Integer[] lastClick = { 0, 0 };
@@ -826,11 +827,11 @@ public class Editor {
                 selectedType = SelectionType.POLYLINE;
                 return picked;
             } else {
-                picked = entityPicker.pickSurface(figureUid, pos);
-                if (picked != null) {
+                selectedSurface = entityPicker.pickSurface(figureUid, pos);
+                if (selectedSurface.surface != null) {
                     selectedType = SelectionType.SURFACE;
                 }
-                return picked;
+                return selectedSurface.surface;
             }
         }
     }
index db639d6..854b4b9 100644 (file)
@@ -47,7 +47,7 @@ public class GEDPicker {
     private final Double delta = 7.0;
     private Integer axesUID;
     private Axes axes;
-    private double Z;
+    private double Z = 2;
 
     /**
      * Picker, given a figure and a click position in pixel coordinates
@@ -262,11 +262,16 @@ public class GEDPicker {
         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]);
 
         Vector3d Dir = v0.minus(v1).getNormalized();
-        Z = 2.0;
-        double curZ = SurfaceData.pickSurface(obj, v0.getX(), v0.getY(), v0.getZ(),
-                                              Dir.getX(), Dir.getY(), Dir.getZ(), mat[2], mat[6], mat[10], mat[14]);
-        if (curZ < Z) {
-            return true;
+        // ray is the parametric cuve t -> v0 + t * Dir
+
+        double t = SurfaceData.pickSurface(obj, v0.getX(), v0.getY(), v0.getZ(), Dir.getX(), Dir.getY(), Dir.getZ());
+
+        if (t !=  Double.NEGATIVE_INFINITY) {
+            Vector3d intersectPoint = new Vector3d(v0.plus(Dir.times(t)));
+            double curZ = intersectPoint.getX() * mat[2] + intersectPoint.getY() * mat[6] + intersectPoint.getZ() * mat[10] + mat[14];
+            if (curZ < Z) {
+                return true;
+            }
         }
         return false;
     }
index 101c69f..04f0916 100644 (file)
@@ -2,6 +2,7 @@
  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
  * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando
  * Copyright (C) 2013 - Scilab Enterprises - Calixte DENIZET
+ * Copyright (C) 2018 - St├ęphane MOTTELET
  *
  * Copyright (C) 2012 - 2016 - Scilab Enterprises
  *
@@ -30,6 +31,10 @@ import org.scilab.modules.graphic_objects.graphicController.GraphicController;
 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
 import org.scilab.modules.renderer.JoGLView.util.ScaleUtils;
+import org.scilab.modules.renderer.CallRenderer;
+import org.scilab.modules.graphic_objects.PolylineData;
+import org.scilab.modules.renderer.utils.EntityPicker;
+import org.scilab.modules.renderer.utils.EntityPicker.SurfaceInfo;
 
 
 /**
@@ -142,47 +147,43 @@ public class DragZoomRotateInteraction extends FigureInteraction {
      */
     private class FigureMouseWheelListener implements MouseWheelListener {
 
-        private void applyZoom(Axes axes, double scale) {
+        private void applyZoom(Axes axes, double scale, double[] position) {
             if (axes != null) {
                 Double[] bounds = axes.getDisplayedBounds();
                 double[][] factors = axes.getScaleTranslateFactors();
+                // Zoom only if position of mouse cursor is inside the bounds
+                if (position[0] > bounds[0] && position[0] < bounds[1] &&
+                        position[1] > bounds[2] && position[1] < bounds[3] &&
+                        position[2] > bounds[4] && position[2] < bounds[5]) {
+                    bounds[0] = position[0] + (bounds[0] - position[0]) * scale;
+                    bounds[1] = position[0]  + (bounds[1] - position[0]) * scale;
+                    bounds[2] = position[1] + (bounds[2] - position[1]) * scale;
+                    bounds[3] = position[1] + (bounds[3] - position[1]) * scale;
+                    bounds[4] = position[2] + (bounds[4] - position[2]) * scale;
+                    bounds[5] = position[2] + (bounds[5] - position[2]) * scale;
+
+                    bounds[0] = bounds[0] * factors[0][0] + factors[1][0];
+                    bounds[1] = bounds[1] * factors[0][0] + factors[1][0];
+                    bounds[2] = bounds[2] * factors[0][1] + factors[1][1];
+                    bounds[3] = bounds[3] * factors[0][1] + factors[1][1];
+                    bounds[4] = bounds[4] * factors[0][2] + factors[1][2];
+                    bounds[5] = bounds[5] * factors[0][2] + factors[1][2];
+
+                    Boolean zoomed = tightZoomBounds(axes, bounds);
+
+                    bounds[0] = (bounds[0] - factors[1][0]) / factors[0][0];
+                    bounds[1] = (bounds[1] - factors[1][0]) / factors[0][0];
+                    bounds[2] = (bounds[2] - factors[1][1]) / factors[0][1];
+                    bounds[3] = (bounds[3] - factors[1][1]) / factors[0][1];
+                    bounds[4] = (bounds[4] - factors[1][2]) / factors[0][2];
+                    bounds[5] = (bounds[5] - factors[1][2]) / factors[0][2];
 
-                double xDelta = (bounds[1] - bounds[0]) / 2;
-                double xMiddle = (bounds[1] + bounds[0]) / 2;
-                bounds[0] = xMiddle - xDelta * scale;
-                bounds[1] = xMiddle + xDelta * scale;
-
-                double yDelta = (bounds[3] - bounds[2]) / 2;
-                double yMiddle = (bounds[3] + bounds[2]) / 2;
-                bounds[2] = yMiddle - yDelta * scale;
-                bounds[3] = yMiddle + yDelta * scale;
-
-                double zDelta = (bounds[5] - bounds[4]) / 2;
-                double zMiddle = (bounds[5] + bounds[4]) / 2;
-                bounds[4] = zMiddle - zDelta * scale;
-                bounds[5] = zMiddle + zDelta * scale;
-
-                bounds[0] = bounds[0] * factors[0][0] + factors[1][0];
-                bounds[1] = bounds[1] * factors[0][0] + factors[1][0];
-                bounds[2] = bounds[2] * factors[0][1] + factors[1][1];
-                bounds[3] = bounds[3] * factors[0][1] + factors[1][1];
-                bounds[4] = bounds[4] * factors[0][2] + factors[1][2];
-                bounds[5] = bounds[5] * factors[0][2] + factors[1][2];
-
-                Boolean zoomed = tightZoomBounds(axes, bounds);
-
-                bounds[0] = (bounds[0] - factors[1][0]) / factors[0][0];
-                bounds[1] = (bounds[1] - factors[1][0]) / factors[0][0];
-                bounds[2] = (bounds[2] - factors[1][1]) / factors[0][1];
-                bounds[3] = (bounds[3] - factors[1][1]) / factors[0][1];
-                bounds[4] = (bounds[4] - factors[1][2]) / factors[0][2];
-                bounds[5] = (bounds[5] - factors[1][2]) / factors[0][2];
-
-                boolean[] logFlags = { axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
-                ScaleUtils.applyInverseLogScaleToBounds(bounds, logFlags);
+                    boolean[] logFlags = { axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
+                    ScaleUtils.applyInverseLogScaleToBounds(bounds, logFlags);
 
-                GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_BOX__, bounds);
-                GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_ENABLED__, zoomed);
+                    GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_BOX__, bounds);
+                    GraphicController.getController().setProperty(axes.getIdentifier(), GraphicObjectProperties.__GO_ZOOM_ENABLED__, zoomed);
+                }
             }
         }
         @Override
@@ -194,8 +195,48 @@ public class DragZoomRotateInteraction extends FigureInteraction {
                 allAxes = getAllUnderlyingAxes(e.getPoint());
             }
             double scale = Math.pow(ZOOM_FACTOR, e.getUnitsToScroll());
+            double[] position = null;
             for (Axes axes : allAxes) {
-                applyZoom(axes, scale);
+                // beware: components of axes.getDisplayedBounds() are log10 of bounds
+                // when axes.get[X,Y or Z]AxisLogFlag() is true
+                Double[] bounds = axes.getDisplayedBounds();
+                Double[] realBounds = axes.getDisplayedBounds();
+                boolean[] logFlags = {axes.getXAxisLogFlag(), axes.getYAxisLogFlag(), axes.getZAxisLogFlag()};
+                ScaleUtils.applyInverseLogScaleToBounds(realBounds, logFlags);
+                // If possible, center the homothecy at a point of a polyline or a point of a surface
+                EntityPicker ep = new EntityPicker();
+                // TODO: picking info should give the Z in order to take the closest object
+                // between polyline and surface
+                Integer polylineUid = ep.pick(axes.getParentFigure(), e.getX(), e.getY());
+                SurfaceInfo surfInfo = ep.pickSurface(axes.getParentFigure(), new Integer[] {e.getX(), e.getY()});
+                if (polylineUid != null) {
+                    EntityPicker.PickedPoint picked = ep.pickPoint(polylineUid, e.getX(), e.getY());
+                    double[] datax = (double[])PolylineData.getDataX(polylineUid);
+                    double[] datay = (double[])PolylineData.getDataY(polylineUid);
+                    double[] dataz = (double[])PolylineData.getDataZ(polylineUid);
+                    // in 3D ep.pick (for PolyLines) does not check that picked point is in the viewing box
+                    if (datax[picked.point] > realBounds[0] && datax[picked.point] < realBounds[1] &&
+                            datay[picked.point] > realBounds[2] && datay[picked.point] < realBounds[3] &&
+                            dataz[picked.point] > realBounds[4] && dataz[picked.point] < realBounds[5]) {
+                        position = new double[] {datax[picked.point], datay[picked.point], dataz[picked.point]};
+                    }
+                } else if (surfInfo.surface != null) {
+                    position = new double[] {surfInfo.point.getX(), surfInfo.point.getY(), surfInfo.point.getZ()};
+                }
+                if (position == null) {
+                    if (axes.getView() == 0) {
+                        // 2D : failsafe homothecy centered at mouse coordinates
+                        double[] pixelPosition = {e.getX(), e.getY(), 0};
+                        position = CallRenderer.get2dViewFromPixelCoordinates(axes.getIdentifier(), pixelPosition);
+                        position[2] = (bounds[4] + bounds[5]) / 2;
+                    } else {
+                        // 3D : failsafe homothecy centered at center of viewing box
+                        position = new double[] {(bounds[1] + bounds[0]) / 2, (bounds[3] + bounds[2]) / 2, (bounds[5] + bounds[4]) / 2};
+                    }
+                }
+                // the zomm is applied in the linear scale, hence coordinates have to be tranformed accordingly
+                ScaleUtils.applyLogScale(position, logFlags);
+                applyZoom(axes, scale, position);
             }
         }
     }
index 39b05a1..99b673e 100644 (file)
@@ -591,47 +591,55 @@ public class EntityPicker {
         return newVec;
     }
 
+
+    public class SurfaceInfo {
+        public Integer surface = null;
+        public Vector3d point = null;
+    }
+
     /*
      * Given a figure search for all plot3d's and for each surface
      * test if the ray given by the mouse position intersects any surface
      * @param figure Figure unique identifier.
-     * @param pos Mouse position (x, y).
-     * @return The nearest surface intersected or null otherwise.
+     * @param mousePos Mouse position (x, y).
+     * @return The SurfaceInfo if picked a surface null otherwise.
      */
-    public Integer pickSurface(Integer figure, Integer[] pos) {
-        Integer uid = AxesHandler.clickedAxes(figure, pos);
+    public SurfaceInfo pickSurface(Integer figure, Integer[] mousePos) {
+        Integer uid = AxesHandler.clickedAxes(figure, mousePos);
         Axes curAxes = (Axes)GraphicController.getController().getObjectFromId(uid);
         if (curAxes == null) {
             return null;
         }
 
         double[][] factors = curAxes.getScaleTranslateFactors();
-        double[] mat = DrawerVisitor.getVisitor(figure).getAxesDrawer().getProjection(uid).getMatrix();
+        //double[] mat = DrawerVisitor.getVisitor(figure).getAxesDrawer().getProjection(uid).getMatrix();
 
-        Vector3d v0 = AxesDrawer.unProject(curAxes, new Vector3d(1.0f * pos[0], 1.0f * pos[1], 0.0));
-        Vector3d v1 = AxesDrawer.unProject(curAxes, new Vector3d(1.0f * pos[0], 1.0f * pos[1], 1.0));
+        Vector3d v0 = AxesDrawer.unProject(curAxes, new Vector3d(1.0f * mousePos[0], 1.0f * mousePos[1], 0.0));
+        Vector3d v1 = AxesDrawer.unProject(curAxes, new Vector3d(1.0f * mousePos[0], 1.0f * mousePos[1], 1.0));
         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]);
         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]);
 
         Vector3d Dir = v0.minus(v1).getNormalized();
-
-
+        // ray is the parametric cuve t -> v0 + T * Dir
         Integer[] types = {GraphicObjectProperties.__GO_PLOT3D__, GraphicObjectProperties.__GO_FAC3D__, GraphicObjectProperties.__GO_GRAYPLOT__};
         Integer[] objs = (new ObjectSearcher()).searchMultiple(figure, types);
-        double Z = 2.0;
-        Integer picked = null;
+        double T = Double.NEGATIVE_INFINITY;
+        SurfaceInfo info = new SurfaceInfo();
         if (objs != null) {
             for (int i = 0; i < objs.length; ++i) {
-                double curZ = SurfaceData.pickSurface(objs[i], v0.getX(), v0.getY(), v0.getZ(),
-                                                      Dir.getX(), Dir.getY(), Dir.getZ(), mat[2], mat[6], mat[10], mat[14]);
-                if (curZ < Z) {
-                    picked = objs[i];
-                    Z = curZ;
+                double curT = SurfaceData.pickSurface(objs[i], v0.getX(), v0.getY(), v0.getZ(), Dir.getX(), Dir.getY(), Dir.getZ());
+                if (curT > T) {
+                    T = curT;
+                    info.surface = objs[i];
                 }
             }
+            if (info.surface != null) {
+                info.point = new Vector3d(v0.plus(Dir.times(T))); // point = v0 + T * Dir
+            }
+            // if formerly used normalized Z \in [-1,1] is to be computed:
+            // Z = info.point.getX() * mat[2] + info.point.getY() * mat[6] + info.point.getZ() * mat[10] + mat[14];
         }
-
-        return picked;
+        return info;
     }