按左右箭头键为MATLAB图形设置动画

时间:2016-03-03 17:43:30

标签: matlab callback pass-by-reference matlab-figure

我正在尝试使用左右箭头键更改球体的半径,并在MATLAB图中绘制。例如,通过按向右箭头,半径值增加1,然后使用更新的半径绘制新球体。同样,按左箭头键可将半径减小一,然后绘制一个较小的球体。但是,我希望此更改仅限于1和rmax之间。

我在阅读this post之后得到了一些关于我如何处理的想法,但它仍然不是我想要的。因此,我使用两个全局变量来完成此任务,以便通过引用KeyPressFcn以某种方式传递信息,因此当按下某个键时KeyPressFcn知道这些限制是什么。在下面的示例中,代码确实将半径增大或减小一个,但是在左箭头和右箭头被击中后,它不会限制半径在指定范围内的变化。

有没有更好的方法来接近这个?如何将radius的值及其限制传递给KeyPressFcn?我希望KeyPressFcn知道在按下左箭头和右箭头时它可以改变半径。

function animateme()

fig_h = figure;
set(fig_h,'KeyPressFcn', @key_pressed_fcn);

global r rmax

p0 = [0 0 0];
[x,y,z] = sphere; 

rmax = 10;
r = 1;

while 1==1

    h = surf(x*r+p0(1), y*r+p0(2), z*r+p0(3)); 
    set(h, 'FaceAlpha', 0.5, 'FaceColor', rand([1 3]))
    axis equal;

    pause

end



function key_pressed_fcn(fig_obj, eventDat)

global r rmax

if strcmpi(eventDat.Key, 'rightarrow')
    r = r + 1;
    if r < 1
        r = 1;
    end
elseif strcmpi(eventDat.Key, 'leftarrow')
    r = r - 1;
    if r > rmax
        r = rmax;
    end
end
disp(r)

1 个答案:

答案 0 :(得分:4)

首先,不要使用全局变量,因为(几乎)总是有更好的方法来完成同样的事情。

这是一个使用嵌套函数的示例,它们可以自动访问父函数工作区中的变量。

function animateme()
    fig = figure();
    hax = axes('Parent', fig);
    set(fig, 'KeyPressFcn', @keypress)

    p0 = [0,0,0];
    [x,y,z] = sphere();

    % Specify limits here which are accessible to nested functions
    rmax = 10;
    r = 1;

    h = surf(x,y,z, 'Parent', hax);

    % Subfunction for re-plotting the data
    % This prevents you from needing a while loop
    function redraw()
        set(h, 'XData', x * r + p0(1), ...
               'YData', y * r + p0(2), ...)
               'ZData', z * r + p0(3));

        set(h, 'FaceAlpha', 0.5, ...
               'FaceColor', rand([1 3]))

        axis(hax, 'equal')
        drawnow
    end

    % Go ahead and do the first redraw
    redraw();

    % Callback to process keypress events
    function keypress(~, evnt)
        switch lower(evnt.Key)
            case 'rightarrow'
                r = min(r + 1, rmax);
            case 'leftarrow'
                r = max(1, r - 1);
            otherwise
                return
        end

        % Always do a redraw
        redraw();
    end
end

另一种选择是使用r字段将UserData的当前值存储在图形对象本身中。所以你可以把它放在surf情节本身。这实际上是我首选的方法,因为您的回调函数可以在任何地方生活,并且仍然可以访问所需的数据。

function animateme()

    fig = figure();

    % Data to store for plotting
    data.p = [0,0,0];
    data.r = 1;
    data.rmax = 10;

    % Create a blank surface for starters
    h = surf(nan(2), nan(2), nan(2));
    set(h, 'UserData', data);

    % Update the display of the surface
    redraw(h);

    % Set the callback and pass the surf handle
    set(fig, 'KeyPressFcn', @(fig, evnt)keypress(h, evnt))
end

function redraw(h)

    % Get the stored data from the graphics object
    userdata = get(h, 'Userdata');

    [x,y,z] = sphere();

    set(h, 'XData', x * userdata.r + userdata.p(1), ...
           'YData', y * userdata.r + userdata.p(2), ...
           'ZData', z * userdata.r + userdata.p(3));

    set(h, 'FaceAlpha', 0.5, ...
           'FaceColor', rand([1 3]))

    axis equal
    drawnow;
end

function keypress(h, evnt)

    % Get the stored data
    userdata = get(h, 'Userdata');

    switch lower(evnt.Key)
        case 'rightarrow'
            userdata.r = min(userdata.r + 1, userdata.rmax);
        case 'leftarrow'
            userdata.r = max(1, userdata.r - 1);
        otherwise
            return;
    end

    % Update the stored value
    set(h, 'UserData', userdata);

    redraw(h);
end