使用回调来识别 3D 图上的鼠标选定点,有时只能在另一个 m 文件中作为函数工作,为什么?

Using a Callback to identify a mouse selected point on a 3-D plot, sometimes only works inside another m-file as a function, why?

提问人:Andrew 提问时间:11/7/2023 最后编辑:Cris LuengoAndrew 更新时间:11/7/2023 访问量:21

问:

在解决“识别 3D 图上的鼠标选定点”问题时,建议使用回调函数来保持对图的交互式关注并选择/识别任意数量的点,我遇到了以下奇怪的行为。

唯一错误地使用欧几里得计算的 Matlab“答案”,所以我将其改编为 Octave,并在此处提供了更好的 3D 最小面积解决方案作为完整代码。

我无法弄清楚的是,当我尝试将代码复制到模糊逻辑文件(非功能)中时,它无法正常工作,在==>“句柄无法识别......”处抛出错误。我把声明放在哪里并不重要。Pt = get(handle.a, "CurrentPoint")handle.a=axes

这里提供的版本与M文件一样适用于函数,但是一旦我将其与其他代码一起放入文件中,而不仅仅是简单的向量语句,如果主文件不是“函数-m-文件”,它就会失败

我让它纯粹是出于猜测,并将“函数”添加到文件顶部并保存它,有人可以解释为什么吗?

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
回调 八度

评论

0赞 Tasos Papastylianou 11/7/2023
克里斯的答案是关于钱的,但如果你想了解更多关于这个话题的细节,请看这里:stackoverflow.com/a/50507980/4183191
0赞 Tasos Papastylianou 11/7/2023
您可能要注意的另一件事是,八度直到最近才支持嵌套函数。因此,如果你有一个旧版本的 Octave,你可能也想检查一下(并转换为另一种需要正确参数的函数)。有关带和不带嵌套函数的交互式图的示例,请参阅此处

答:

2赞 Cris Luengo 11/7/2023 #1

在发布的代码中,是一个嵌套函数。作为嵌套函数,它可以访问父函数中定义的变量。click

如果删除文件顶部的行,则 M 文件是脚本,函数是本地函数。作为局部函数,它无权访问自身外部定义的变量。在函数之前或之后在脚本中定义的任何变量在函数中都不可见。functionclick

但请注意,Octave 不允许在脚本 M 文件的末尾使用本地函数,因此在使用时不会定义。在MATLAB中,它可以工作,但在Octave中则不行。在 Octave 中,您必须先将函数声明移动到脚本中的某个点,然后才能使用它。click

在这种形式中,您必须将句柄作为参数传递到函数中。例如,您可以定义

function click(handle,src)

然后,作为回调传递的句柄将是 。@click@(src,~)click(handle,src)

评论

0赞 Andrew 11/8/2023
我确实看到过一次“不支持嵌套函数”错误,但这个答案和 Tasos 给出的链接使其更容易理解。