ginput and 'CurrentPoint' property do no match real axes coordinates

35 views (last 30 days)
André on 15 Jun 2020
Edited: Adam Danz on 14 Oct 2020
This is a very strange problem. I am creating a GUI with many overlapping patches objects. I also changed the XLim and YLim properties to make the axes square and to show all the patches. As the mouse moves over the axes, I use the CurrentPoint property to display the current mouse coordinates. But it seems the axes is offset to the right by a certain amount. If I plot a marker at 0,0, it is correclty plotted at the center of the axes. However, if I use ginput() at the center of the axes, it does not return (0,0), it instead returns (0.3185, 0).
I have noticed that the number 0.3185 is a multiple of 0.1593 and that ginput always returns multiples of 0.1593. So I believe 0.1593 is the axes maximum resoution, accordingly to the axes limits I am currently using.
Can someone explain why there is this offset of 0.3185 in the x-direction of my axes? And why is the data plotted correclty, while ginput and 'CurrentPoint' give wrong results?
Im using R2017b
Regards,
André
André on 15 Jun 2020
Edited: André on 15 Jun 2020
Yeah, the ammount of error depends on the limits you are using. The wider the limits, the larger the error value.
Did you notice if the values you obatin using ginput() are common multiples of a base value?
Edit: I tried now with limits from [-1000, 1000]
Using ginput at the origin, I obtain
2.3041 -0.0000
I did some measurements around the origin and obtained the following:
2.3041 -0.0000
2.3041 -5.8309
6.9124 -5.8309
16.1290 -5.8309
11.5207 -11.6618
6.9124 -11.6618
If I divide by the first element, I obtain:
1.0000 -0.0000
1.0000 -2.5306
3.0000 -2.5306
7.0000 -2.5306
5.0000 -5.0612
3.0000 -5.0612
In the x-direction all values are multiples of 2.3041 (which is 1000 times the value you obtained, so the error is not yours).
I also tested the 'CurrentPoint' property of the axes (using a cross plotted at the origin to guide me) and obtained exacty the same results.
ans =
2.3041 -0.0000 1.0000
2.3041 -0.0000 -1.0000
This clearly demonstrates that there is a bias with ginput() and the 'CurrentPoint' property in the x-direction. On the other hand, the y-direction seems to be OK, despite the values seeming to be now multiples of 5.8309, which proves that the resolution of the axes is different for each direction.

Adam Danz on 16 Jun 2020
Edited: Adam Danz on 14 Oct 2020
Written in r2020a update 3
Symptoms of the problem
The current mouse position within an axes (ax.CurrentPoint) has a limit of precision such that the true (0,0) coordinate (and other specific coordinates) cannot be manually selected from the CurrentPoint property along the x axis.
Demonstrating the problem
The code below creates a square axes with axis limits from -1:1 and dashed crosshairs at x=0 and y=0 for reference. The figure contains a WindowButtonMotionFcn that updates text in the upper, left corner of the axes indicating the current mouse position within the axes. A second line of text ("delta") shows the distance between the previous and current mouse position. It also contains blue crosshairs that follow the current mouse position.
With a sensitive mouse and some patience, align the blue crosshairs with the reference lines at (0,0) and you'll notice that the current y-coordinates matches y=0 but the current x-coordinate cannot match x=0.
figure()
ax = axes('FontSize', 20, 'XTick', -1:.2:1, 'YTick',-1:.2:1);
axis equal
xlim([-1 1])
ylim([-1 1])
xline(0, 'k--')
yline(0, 'k--')
grid on
th = text(min(xlim), max(xlim), '', 'FontSize', 16, 'VerticalAlignment', 'top');
crosshairs(1) = xline(max(xlim),'b--');
crosshairs(2) = yline(max(ylim),'b--');
set (gcf, 'WindowButtonMotionFcn', {@mouseMove, th, ax, crosshairs});
hold on
drawnow
function mouseMove(~, ~, th, ax, crosshairs)
persistent cursorPos
if ~isempty(cursorPos)
delta = abs(cursorPos - ax.CurrentPoint(1,1:2));
else
delta = [nan, nan];
end
cursorPos = ax.CurrentPoint(1,1:2);
th.String = sprintf('(%.5f, %.5f)\n(%.5f, %.5f) delta', ax.CurrentPoint(1,1:2), delta);
crosshairs(1).Value = ax.CurrentPoint(1,1);
crosshairs(2).Value = ax.CurrentPoint(1,2);
end
In the example below, the cursor is as close as possible to (0,0) and the error between the perceived coordinate and the actual selected coordinate is at 0.00122.
Magnitude and direction of the error
The magnitude and direction of the error between the perceived selected coordinate and the actual selected coordinate depends on
• Axis size
• Figure size (when axes resize with the figure)
• Axis limits
• Axis aspect ratio
• Monitor resolution
• Location of the selected coordinate within the same axes.
Change any of those properties and the magnitude and direction of the error will likely change. Since the error changes with different coordinates within the exact same axes, the magnitude and direction of error cannot be corrected. However, zooming into an area of interest and reducing the range of axis limits will decrease the magnitude of the error.
The cause of the problem
The precision of coordinate selection is limited by the arrangement of pixels on your monitor. When you select a point, you are selecting a pixel. That pixel will often have a relatively small offet from the actual coordinate you intended to select. Zooming into the axes is equivalent to decreasing the pixel size relative to the axes which increases precision. The problem is analgous to roundoff error.
Going back to the demo figure above, the mouse is as close as possible to (0,0) but the current coordinate reads (0.0012, 0.0000). If we add a marker at that location and another marker at (0,0), the two coordinates are indistinguishable while holding the axis and figure properties constant.
plot(0.0012, 0.0000, 'rx','MarkerSize', 20)
plot(0.0000, 0.0000, 'bx','MarkerSize', 12)
You can see that blue x is directly on the red x even though they have slighly different coordinates. Then, zoom into that point and you will see that they separate. This demonstrates the limit or precision at different axis scales.
Zoom into the axes, the difference between the two coordinates becomes visible.
xlim([-.01, .01])
ylim([-.01, .01])
Solution to the problem
In short, there is no simple solution. I suppose MathWorks could recalibrate the bias such that (0,0) can always be selected, but that would only fix the error at that coordinate.
Since the error is different for different coordinates within the same identical axes, you cannot simply measure a constant bias and offset all selected coordinates by that bias.
The only way to increase precision is by zoom into the region of interest and selected the coordinate within a narrow range of axis limits.
If selecting specific targets such as (0,0) is important, you could compute the distance between the CurrentPoint and all targets and select the target closest to the point.
Lastly, buying a monitor with higher resolution will also decrease, but not eliminate, the error.
Adam Danz on 19 Jun 2020
@André , I heard back from the developers who confirmed my suspicions.
"The issue starts with rounding of the point to the nearest pixel, and there are inherent discretizations in the internal calculation of CurrentPoint which prevent the user from being able to select this [or any] specific point." I added the part in brackets.
This is related to the roundoff error which I referenced (and provided a link) in my answer.
That also explains why the error between the perceived selected point and the actual selected pixel will not be the same for each coordinate in the axes. If you are offsetting the values by a constant rate, you are adding error to the selected values.
I can't emphasize this enough. The only want to reduce the error is by zooming into the regon of interest. Subtracting (or adding) a constant to every selected point only minimizes error at the coordinate where the "bias" was measured.