4

I have come into a problem recently when trying to rotate 3D objects. I am building a GUI and I have a separate figure where an object is plotted. In the figure, I allow the user to use MATLAB's built-in rotate button to move the object around. However, I am unable to have the light also follow the rotation as it seems to be fixed on one part of the object. To create the light, I am using

c=camlight('right');
set(c,'style','infinite');

One solution I have thought of is to add light whenever the user releases the rotate button, but this is not very nice. I also do not know how to use the rotate callbacks when the button is in a separate figure.

Does anyone know how to make the light "track" the 3D rotation, so that the current view is illuminated?

Thanks!

maz
  • 53
  • 5

1 Answers1

5

Implement own rotation functionality

You can implement an own functionality to adjust the axes and the lights at the same time. This way the light gets adjusted continuously while rotating the axes.

function follow_me_1
    figure
    axes('buttondownfcn', @buttondownfcn);  % assign callback
    set(gca,'NextPlot','add');              % add next plot to current axis
    surf(peaks,'hittest','off');            % hittest -> off is important
    view(3);                                % view to start from
    c = camlight('headlight');              % add light
    set(c,'style','infinite');              % set style of light

    function buttondownfcn(ax,~)
        fig = ancestor(ax,'figure');        % get figure handle
        [oaz, oel] = view(ax);              % get current azimuth and elevation
        oloc = get(0,'PointerLocation');    % get starting point
        set(fig,'windowbuttonmotionfcn',{@rotationcallback,ax,oloc,oaz,oel});
        set(fig,'windowbuttonupfcn',{@donecallback});
    end

    function rotationcallback(~,~,ax,oloc,oaz,oel)
        locend = get(0, 'PointerLocation'); % get mouse location
        dx = locend(1) - oloc(1);           % calculate difference x
        dy = locend(2) - oloc(2);           % calculate difference y
        factor = 2;                         % correction mouse -> rotation
        newaz = oaz-dx/factor;              % calculate new azimuth
        newel = oel-dy/factor;              % calculate new elevation
        view(ax,newaz,newel);               % adjust view
        c = camlight(c,'headlight');        % adjust light
    end

    function donecallback(src,~)
        fig = ancestor(src,'figure');           % get figure handle
        set(fig,'windowbuttonmotionfcn',[]);    % unassign windowbuttonmotionfcn
        set(fig,'windowbuttonupfcn',[]);        % unassign windowbuttonupfcn
    end

end

Using rotate3d

This example uses the built-in rotate3d and assigned callback functions. This is the 'not very nice'-solution but takes only some lines of code.

function follow_me_2
    surf(peaks);                  % Load demo data
    c = camlight('headlight');    % Create light
    set(c,'style','infinite');    % Set style
    h = rotate3d;                 % Create rotate3d-handle
    h.ActionPostCallback = @RotationCallback; % assign callback-function
    h.Enable = 'on';              % no need to click the UI-button

    % Sub function for callback
    function RotationCallback(~,~)
        c = camlight(c,'headlight');
    end
end
Matt
  • 12,848
  • 2
  • 31
  • 53
  • Thank you very much! I will try these out. I had implemented something similar to the rotate3d solution you proposed last night but the lighting changes are a bit sloppy. I will try the first solution you proposed. – maz Jun 19 '15 at 13:56
  • You're very welcome! I saw your other post and thought you will try the solution with `rotate3d` because you want to handle zoom as well. The first solution works as well with zoom! You just have to write an own zoom function and make the assignment of `windowbuttonmotionfcn` dependent of a control element in your gui. This way you can change between zoom and rotation very easily. – Matt Jun 19 '15 at 14:49
  • I tried your first solution and I liked it more than the second. However, my issue is that I would like it to work when the user presses the rotate button or the zoom button on the figure toolbar. I tried it using rotate3d but it was a bit choppy and was not smooth. Do you know if there is a way to keep that button but to use your implementation instead? – maz Jun 20 '15 at 14:59
  • if you use implementation 1, make sure you are not in rotate3d mode (e.g., do not use the rotate camera button on the cameratoolbar) – Jed Jan 13 '20 at 14:09