Working on the "Identifying a mouse selected point on a 3-D plot" problem the Callback function is used and recommended to retain interactive focus on a plot and select/identify any number of points, I ran into the following odd behaviour.
The only Matlab "answer" that uses Euclidian calculations erroneously, so I adapted it for Octave and I supply a better 3-D minimum-area solution here as complete code.
Something I couldn't figure out is when I tried to copy the code into a Fuzzy logic file, non-function, m-file it failed to work throwing errors at the Pt = get(handle.a, "CurrentPoint")
==> "handle is not recognised..." It matters nothing where I put the handle.a=axes
declaration.
The version provided here works just as well with the M-file as a function or not but as soon as I put it in a file with other code more than just simple vector statements, it fails if the main file is not a "function-m-file"
I made it work out of pure guess work and added the "function" to the top of the file and saved it, can someone explain why?
function Interactive_surf
format short;
handle.a = axes;
Xt = [0 0.71429 1.4286 2.1429 2.8571 3.5714 4.2857 5 5.7143 6.4286 7.1429 7.8571 8.5714 9.2857 10];
Yt = [0 0.71429 1.4286 2.1429 2.8571 3.5714 4.2857 5 5.7143 6.4286 7.1429 7.8571 8.5714 9.2857 10];
Z = [ 6.2518 6.4574 7.0428 8.1286 9.3374 10.149 10.435 10.462 10.435 10.531 11.293 12.839 14.343 14.955 15;
7.0713 7.2578 7.7689 8.7243 9.7992 10.528 10.786 10.81 10.786 10.542 11.306 12.856 14.362 14.971 15.015;
8.2585 8.432 9.0076 9.789 10.672 11.272 11.486 11.506 11.486 11.272 11.516 13.118 14.652 15.218 15.247;
9.7942 9.9353 10.715 12.178 12.698 13.041 13.161 13.172 13.161 13.041 12.698 14.456 16.12 16.419 16.386;
11.446 11.556 12.295 14.137 16.275 16.06 15.987 15.98 15.987 16.06 16.275 17.449 19.288 18.78 18.699;
12.902 12.988 13.682 15.392 17.287 18.291 18.598 18.572 18.598 18.877 19.709 21.084 22.69 21.138 21.166;
13.936 14.007 14.664 16.27 17.992 18.872 19.229 19.401 19.481 19.815 20.77 22.286 23.469 22.158 22.181;
14.532 14.594 15.229 16.77 18.394 19.207 19.537 19.703 19.781 20.116 21.061 22.519 23.532 22.807 22.827;
14.819 14.878 15.5 17.01 18.586 19.368 19.686 19.848 19.925 20.26 21.198 22.625 23.602 23.454 23.469;
14.937 14.993 15.611 17.108 18.665 19.434 19.747 19.907 19.984 20.319 21.253 22.667 23.962 24.001 24.011;
14.979 15.035 15.652 17.143 18.694 19.457 19.769 19.929 20.005 20.34 21.273 22.682 23.97 24.394 24.396;
14.995 15.051 15.667 17.157 18.704 19.466 19.777 19.937 20.013 20.348 21.281 22.688 23.973 24.651 24.651;
15 15.056 15.671 17.161 18.707 19.469 19.78 19.939 20.016 20.351 21.283 22.69 23.974 24.674 24.803;
15 15.056 15.671 17.161 18.707 19.469 19.78 19.939 20.016 20.351 21.283 22.69 23.974 24.674 24.89;
15 15.056 15.671 17.161 18.707 19.469 19.78 19.939 20.016 20.351 21.283 22.69 23.974 24.674 24.922];
## Assign raw data to the handle structure
handle.x = Xt;
handle.y = Yt;
handle.z = Z; ## NOTE the Z-matrix has columns as X-axisvalues and
### Plot in 3D with CallBack function called
handle.p = surf(handle.x,handle.y,handle.z, 'ButtonDownFcn', {@click});
xlabel('x-axis');
ylabel('y-axis');
zlabel('z-axis');
grid on
function click(src,~)
## Get current perpendicular screen vector
Pt = get(handle.a, "CurrentPoint")
### Sort through every point in the surface to find the smallest Area to the point
for dy= 1:15
for dx = 1:15
surf_Pt(dx, :) = [handle.x(dx) handle.y(dy) handle.z(dy, dx)];
v1 = surf_Pt(dx, :) - Pt(1,:); ## Position vectors: line vector end point to
v2 = surf_Pt(dx, :) - Pt(2,:); ## existing surface grid points
X_Prod = cross(v1, v2); ## Vector Cross product gives normal vector
Mag_X_Prod = norm(X_Prod); ## |normal vector|==> Area of //pipehead
Area(dx, dy) = 0.5*Mag_X_Prod; ## Store each area of triangle
endfor
endfor
[row, col] = find(Area == min(Area(:)))
str = 'min_Area = %d';
str1 = sprintf(str, Area(row, col));
disp(str1)
# Display the selected point on 3D plot.
# Points on the fence may not generate a label,
# so rotate the image so as it shows inside the grid, then click.
formatSpec = "Pt clicked = [%d %d %d]";
point_text_val = [handle.x(row) handle.y(col) handle.z(col, row)];
pos_text_z = (handle.z(col, row))+0.2;
str = sprintf(formatSpec, point_text_val)
text(handle.x(row), handle.y(col), pos_text_z, str,'FontSize',18)
## NOTES
## The figure properties "alphamap" would be used to modify the transparency
## of the axes frame and allow visiblity of the labels added above that
## run off behind the 100%-white fence SADLY
## "Transparency is not yet implemented for figure objects. alphamap is unused"
endfunction
end
In the code as posted, click
is a nested function. As a nested function, it has access to the variables defined in the parent function.
If you remove the function
line at the top of the file, then the M-file is a script, and the function click
is a local function. As a local function, it does not have access to variables defined outside of itself. Any variables defined in the script before or after the function are not visible inside the function.
But note that Octave doesn’t allow local functions at the end of script M-files, and so click
will not be defined when used. In MATLAB it would work, but not in Octave. In Octave you’d have to move the function declaration to some point in the script before you use it.
In this form, you’d have to pass the handle into the function as an argument. For example you could define
function click(handle,src)
And then instead of @click
, the handle you pass as a callback would be @(src,~)click(handle,src)
.