This short code plots a flat patch (adapted from Matlab's manual):
t = 0:pi/5:2*pi;
figure
x=sin(t); y=cos(t); z=ones(size(x));
patch(x,y,z,'y')
axis equal
This results with a nice patch facing the camera:
However, I want to plot this patch on top of an already plotted 3D object, with the camera set on a fixed arbitrary view. My question is: how to rotate the patch's coordinates so the patch will face the camera and will have the right roll? A correct solution will make the patch look the same from any given arbitrary view (uniform scaling is permitted).
I guess that the camera's position, target and up-vector have to be taken into account, but it's not clear to me how.
It's funny, but today I was doing exactly the same thing :)
My approach is the following
Put your camera somewhere on the positive half of the X axis and set camera target to (0,0,0)
Draw your object the way you need it
Store X, Y, and Z coordinates of your object
Use rotate3d function and the make callbacks for ActionPreCallback and ActionPostCallback events of the rotate3d mode object
You'll need also to provide your routine for 'WindowButtonMotionFcn' event of the active figure
The whole logic works like this.
After you clicked rotate3d icon or call the rotate3d function, right after you click the left button on axes to start rotation, the ActionPreCallback event is fired. There you have to set flag (WeAreRotating in the code below) that rotation started. Then in the 'WindowButtonMotionFcn' callback function you retrieve camera view position using
[az,el] = view(ah);
function and rotate your object. The rotation is tricky, because you have to set its X, Y, and Z coordinates to the original ones you stored before and apply the rotate function to it. Something like this:
function fig_WindowButtonMotionFcn_callback(obj,evd)
if getappdata(gca,'WeAreRotating')
newView = round(get(gca,'View'));
set( ObjectHandle, ...
'XData',XData0, ...
'YData',YData0, ...
'ZData',ZData0 );
rotate( ObjectHandle, [1 0 0],-newView(2), RotationOrigin );
rotate( ObjectHandle, [0 0 1],+newView(1), RotationOrigin );
end
end % if FogProps.SimpleWhenRotated
The ActionPostCallback event is triggered when you release the mouse button and finish rotation. There you have to clear the rotation flag so moving your mouse will not change the object until you start rotation again.
Sorry if my explanation is somewhat unclear.
It is a little tricky to arrange all the flags properly, especially if you have several axes on the figure.
Actually, after your question I decided to clean my code and post it to FEX so everyone can use it so there you'll see how I achieved desired behavior.
UPDATE1
See http://www.mathworks.com/matlabcentral/fileexchange/47275-fog3d for full example
Related
I have a 3-D plot which I want to cut somehow to show the most interested part and avoid the flat parts (as shown in the picture the blue and orange parts to be the least). I think that it can be done using change of the axis limits in x but different for x_{back} and x_{front} which means I want to change the limits of x front axis to (-20,20) and x back to (-80,-40). How can I do this?
I think krisdestruction is right, it would be such an infrequently-used feature that it's probably not worth the development time or added complexity for TMW to implement.
But you could kludge it. If you were to rotate your data so that the feature aligns with the axes then you could crop the plot to the region of interest as desired. Then hide the grid and draw a new one yourself.
If you're careful you can arrange it so that you can still use the axis labels at the front, which will save you some time, but if not you can always use text to draw new ones on.
I would rotate the data using a rotational transform matrix, which will be pretty quick, and you might be able to pull the gridlines out of the gca object and apply the rotation matrix to those too, which would save you from having to compute them all explicitly.
If you expect to do this more than once or twice then you could encapsulate it all in a nice function that works out the rotation angle from a given pair of 'front' and 'back' axis limits.
Then you can post it to the file exchange : )
have an image that I would like to zoom in/out when I left/Right-click a point on the image but I do not want to do it using the magnifying glass. Basically, I need a script for what I said above. I have come up with the following script but it only zooms in/out to the center not to the position that I click on.
And if you are asking why I am getting the clicked position using ginput the answer is, I am planning to use this script to edit a binary image! I have removed the lines corresponding to the binary image editing part to avoid any confusion.
hFi= figure; imshow(e);
button=0;
while button~=2
% Get the mouse position on the axes (needed for binary image editing) and button number
[y,x,button]=ginput(1);
% Get the mouse position on the figure
position=get(hFi,'CurrentPoint')
% Set 'CurrentPoint' to the mouse position that was just captured
set(hFi,'CurrentPoint',position)
% Determine if it is a zoom-in or zoom-out
if button==1
zoom(2);
elseif button==3
zoom(0.5);
end
end
I think the command set() does not do anything here but I saw someone suggested that in a forum. Sorry if that looks dumb!
I am assuming you have a matrix as you are asking for the y and x values.
In this way a simple answer would be to display a smaller, or a larger range of values.
Let A be your matrix.
A=rand(1000,1000);
figure,
h=imagesc(A)
axis off
axis image
colormap('gray')
A_range=size(A);
A_zoom=1;
while true
[y,x,button]=ginput(1)
A_center=[x,y];
if button==1
A_zoom=A_zoom/2;
elseif button==3
A_zoom=A_zoom*2;
end
axis([max(x-A_range(1)*A_zoom,1), min(x+A_range(1)*A_zoom,A_range(1)), ...
max(y-A_range(2)*A_zoom,1), min(y+A_range(2)*A_zoom,A_range(2))])
end
Alright, after playing around with imshow and reading through the documentation on internet, I finally came up with what I had been looking for. Thanks to everyone who suggested some ideas, especially ASantosRibeiro who gave me the idea of changing the axis limits.
Basically, I used a combination of commands zoom and xlim/ylim. Therefore, the final code would look like:
hFi= figure; h=imshow(e);
button=0;
zlvl=1;
xl = get(gca,'xlim');
xlen = size(e,2);
yl = get(gca,'ylim');
ylen = size(e,1);
while button~=2
% Get the mouse position on the axes (needed for binary image editing) and button number
[y,x,button]=ginput(1);
% Determine if it is a zoom-in or zoom-out
if button==1
zlvl = zlvl*2;
zoom(2);
elseif button==3
zlvl = zlvl/2;
if zlvl<1, zlvl=1; end % No zoom level smaller than 1
zoom(0.5);
end
% Change the axis limits to where the mouse click has occurred
% and make sure that the display window is within the image dimensions
xlimit = [x-xlen/zlvl/2+0.5 x+xlen/zlvl/2+0.5];
if xlimit(1)<0.5, xlimit=[0.5 xlen/zlvl+0.5]; end
if xlimit(2)>0.5+xlen, xlimit=[xlen-xlen/zlvl+0.5 xlen+0.5]; end
xlim(xlimit);
ylimit = [y-ylen/zlvl/2+0.5 y+ylen/zlvl/2+0.5];
if ylimit(1)<=0.5, ylimit=[0.5 ylen/zlvl+0.5]; end
if ylimit(2)>=0.5+ylen, ylimit=[ylen-ylen/zlvl+0.5 ylen+0.5]; end
ylim(ylimit);
end
This code lets you zoom in/out when you use ginput and you need to use imshow to keep the aspect ratio of image fixed. This becomes very helpful when you need to read coordinates of image pixels and zoom in/out as well. It zooms in when the user left-clicks and zooms out when the user left-clicks. If the middle button is clicked the while loop ends. Other buttons can be added to perform desired operations. For example, I added a few lines of code to edit binary images.
I would like to know your thoughts and if there is any way to make this code more efficient.
I'm creating a game (similar to Space Invaders) in MATLAB using Guide, where the player's ship is controlled by an accelerometer. So far, my GUI is nearly complete and I have managed to import an image of a ship and move it along the x-axis of main axes with the accelerometer. However, I cannot get enemy ships to be generated on the axes at the same time. After adding just a second image, the first one never generates. The code that loads the images is as follows:
handles.spaceShipImg = flipdim(imread('spaceship.jpg'),1);
handles.enemyShipImg = flipdim(imread('enemy1.jpg'),1);
Here is the part where I attempt to display the images:
handles.step = handles.step + handles.gx; %handles.gx is the reading from the accelerometer
axes(handles.magaxes)
image([handles.gx+handles.step 0.7+handles.gx+handles.step],[0 0.7],handles.spaceShipImg);
image([8 8.7],[8 8.7],handles.enemyShipImg);
set(gca,'YDir','normal')
axis([0 10 0 10]);
I'm wondering if it is possible to just use rectangular objects, and then paint them with an image instead. I also have to create collision detection, and I'm not sure how to implement that with images or rectangles. Any help is appreciated.
Try using the hold on and hold off commands to prevent the axes from refreshing when painting the 2nd image. Put the hold on after axes(handles.magaxes) and use 'hold off` after axis([0 10 0 10]);
Is there some way of using the surf() style data format to create a heat map?
I have a bunch of scattered data of the form z=f(x,y) (so I used TriScatteredInterp to make it work with meshgrid) and I'd like to visualize z with a heat map. surf already does something similar to what I want, except you have to manually rotate the graph so the view is top down looking at the XY plane.
Basically, I'd like something similar to this:
But surf gives you this by default:
Although the answers here already show how to do this with surf, rendering the 3d surface feels a little like overkill...
pcolor creates the required images directly
(with slightly better results -surf has a gap next to the axes)
code
figure(1)
pcolor(peaks)
figure(2)
surf(peaks)
view(2)
results
pcolor
surf
Adding to Ben's answer, you can use the view command. view allows you to rotate your plot to whatever camera angle you wish.
In general, you call the command like so:
view(AZ, EL);
AZ is the azimuth or horizontal rotation, while EL is the vertical elevation. These are both in degrees.
In your case, once you plot your surf plot, use view(0, 90); before you go to the next subplot. view(0, 90); is the default 2-D view, and this looks directly overhead.
By doing this, you avoid having to rotate your plot manually, then using campos to determine what the camera position is at given your plot. view(0, 90); should give you what you need.
Sidenote
Doing view(2); also gives you the default 2D view, which is equal to view(0, 90); as we talked about. By doing view(3);, this gives you the default 3D view as seen in your plots. FWIW, the default azimuth and elevation for a 3D plot is AZ = -37.5, EL = 30, in degrees of course.
Rotate the view to what you want. Then type campos in the terminal. This shows you the camera position. You can then use campos( your_desired ) to set the camera position for future plots.
For example, the x, y view is usually:
campos([4.0000 2.5000 86.6025])
I would like to change the view of a 3D plot in matlab such that the y-axis points upward and the z-axis points to left. For example, consider the following plot:
Here the x-axis points forward, the y-axis points to the right and the z-axis points upward.
I would like to have the y-axis points upward and the z-axis points to the left instead. I tried to rotate the plot (using the figure window toolbar rotate button) but I could not get it to work. (It should just be a simple 90 degrees rotation about the x-axis)
Code to generate the plot:
membrane
view(100,50)
xlabel('x-axis');
ylabel('y-axis');
zlabel('z-axis');
grid on
Try using view. I don't have MATLAB available so I can't test it, but I think it can do what you want.
Example from the documentation:
Set the view along the y-axis, with the x-axis extending horizontally
and the z-axis extending vertically in the figure.
view([0 0]);
EDIT:
Try using three inputs to the view function. I can't experiment myself, but you should be able to do it if you choose the right values here.
From documentation:
view([x,y,z]) sets the view direction to the Cartesian coordinates x,
y, and z. The magnitude of (x,y,z) is ignored.
EDIT 2:
Check out camroll. I think camroll(90) (possibly in combination with view) will work.
From documentation:
camroll(dtheta) rotates the camera around the camera viewing axis by
the amounts specified in dtheta (in degrees). The viewing axis is the
line passing through the camera position and the camera target.
This was posted a while ago, but in case someone else is looking for ways to set y-axis as the vertical one here is a possible fix.
Manually: In the command window type cameratoolbar('show') which will open an interactive toolbar in your plot from which you could change the view. One of the options is to set a principle axis to x, y, or z.
Or in you script you could use cameratoolbar('SetCoordSys',coordsys) command which sets the principal axis of the camera motion. coordsys can be: x, y, z, or none.
http://uk.mathworks.com/help/matlab/ref/cameratoolbar.html