I'm trying to plot a square on Matlab, specifically using the line command, with corners at the points (0,0), (0, rho), (rho, 0) and (rho, rho)
% create axes
x = linspace(0,10,100);
y = linspace(0,20,100);
rho = 2*pi;
% plot
figure;
A = line([0 0],[0 rho]);
B = line([0 0],[rho 0]);
C = line([0 rho],[rho rho]);
D = line([rho 0],[rho rho]);
fill(A,B,C,D,'b');
However, line D fails to appear in my figure and moreover, the fill command is not working, although that part is really quite optional. My main issue is why the aforementioned line won't appear in the plot
That's because you aren't specifying the coordinates for the box properly. Remember that line takes in two vectors where the first vector is a list of x coordinates and the second vector is a list of y coordinates. Each ith pair of (x, y) will have a line drawn from its previous (i-1)th point up to the ith point except for the first point of course. The lines drawn by A and B are the same line. The same can be said with C and D. It's just a matter of modifying the statements so you're drawing the line correctly.
Drawing the box in lovely ASCII graphics for illustration:
(0, rho) (rho, rho)
------------------------
| |
| |
| |
| |
------------------------
(0, 0) (rho, 0)
You need to draw four lines. Let's traverse counter-clockwise:
Going from (0, 0) to (0, rho)
Going from (0, rho) to (rho, rho)
Going from (rho, rho) to (rho, 0)
Going from (rho, 0) to (0, 0)
Therefore, modify your code to be:
rho = 2*pi;
A = line([0 0],[0 rho]);
B = line([0 rho],[rho rho]);
C = line([rho rho],[rho 0]);
D = line([rho 0],[0 0]);
BTW, the code above may not be portable for later. You are going to get line handles which is fine by you don't use this as an input into fill.
We finally get:
It may be cleaner to just put all of the coordinates in just two vectors and call line. This will also make this play nicely with fill:
rho = 2*pi;
x = [0, 0, rho rho, 0];
y = [0, rho, rho, 0, 0];
line(x, y);
hold on;
fill(x, y, 'b');
Notice that we use the proper convention for line, then draw it, then we fill it. If you follow the logic specified earlier, we draw a line from (0, 0) to (0, rho), then from (0, rho) to (rho, rho), then from (rho, rho) to (rho, 0) then finally from (rho, 0) back to (0, 0). Notice that we had to use the origin (0, 0) at the beginning and at the end to ensure that we draw the line at the bottom edge of the square. We also use hold on to add the filled box in after the square boundary that you've drawn on the figure. fill takes in a vector of coordinates just like line does. We get:
You have made a number of mistakes regarding the input arguments for line and fill. Firstly, the inputs to line are the x coordinates for the line points followed by the y coordinates for the line points, NOT successive pairs of (x,y) points. The following will plot your square correctly, starting at (0,0) and drawing lines clockwise:
A = line([0 0], [0 rho]); % Left edge
B = line([0 rho], [rho rho]); % Top edge
C = line([rho rho], [rho 0]); % Right edge
D = line([rho 0], [0 0]); % Bottom edge
The values returned are handles to the line graphics objects. These can be used to modify the line properties, but you can't pass these to fill. You should instead pass polygon vertex data.
There is an easier way to handle all of this, though. You can instead define a vector of x and y coordinates for the vertices of your square, making it much easier to plot lines and filled polygons:
X = [0 0 rho rho 0];
Y = [0 rho rho 0 0];
hLine = line(X, Y);
hold on; % Needed to add to existing plot instead of erasing
fill(X, Y, 'b');
Related
Applying 3D rotation matrix to the x,y,z values obtained from surface function object. The error I get is due to the matrix not being nonconforment but how can I adjust the matrix correctly?
I know hgtransform / makehgtform can do rotations but I need to use rotation matrices since I plan on testing it using matrices created from quaternions.
I've created a little plane out of cylinders and the surface functions.
See code below:
clear all,clf
ax=axes('XLim',[-2 2],'YLim', [-2 10],'ZLim',[-1.5 1.5]);
grid on;
%axis equal;
xlabel('x');
ylabel('y');
zlabel('z');
ax
% rotate around
rot_mat = [.707 -.707 0;.707 .707 0; 0 0 1] %rotation matrix
[xc yc zc] = cylinder([0.1 0.0]); %cone
[x y z]= cylinder([0.2 0.2]);
h(1) = surface(xc,zc,-yc,'FaceColor', 'red'); %noise cone
h(2) = surface(z,y,0.5*x,'FaceColor', 'blue'); %right wing
h(3) = surface(-z,y,0.5*x,'FaceColor', 'yellow');%left wing
h(4) = surface(x,-1.5*z,0.5*y,'FaceColor', 'green'); %main body
h(5) = surface(xc,(1.5*yc)-1.3,z*.5,'FaceColor', 'red'); %tail
view(3);
x_temp = get(h(1),'xdata'); % get x values
y_temp = get(h(1),'ydata');
z_temp =get(h(1),'zdata');
xc_new=x_temp.*rot_mat;
%zc_new=
%yc_new=
I can get the x,y, and z value by using the commands
x_temp = get(h(1),'xdata');
y_temp = get(h(1),'ydata');
z_temp = get(h(1),'zdata');
The error I get is due to the matrix being nonconforment but how can I adjust the matrix correctly?
error: test_object_matrix_rot: product: nonconformant arguments (op1 is 2x21, op2 is 3x3).
The error is with the line xc_new=x_temp.*rot_mat;
PS: I'm using Octave 5.0.91 which is like Matlab
YOu are messing up a lot of things......in fact I would say, you have made your work complex. YOu should straight away work on matrices to rotate to new positons instead of arrays and picking them from the figure.
This line:
x_temp = get(h(1),'xdata'); % get x values
giving you a 2*21 array and your rot_mat is 3X3.....you cannot multiply them. YOu need to pick (x,y,z) and multiply this point with rotation matrix to get the point shifted. Check the below pseudo code.....yo can develop your logic with the below example code.
t = 0:0.1:1;
[X,Y,Z] = cylinder((t));
%% Rotation
th = pi/2 ;
Rx = [1 0 0 ; 0 cos(th) -sin(th) ; 0 sin(th) cos(th)] ;
P0 = [X(:) Y(:) Z(:)] ;
P1 = P0*Rx ;
X1 = reshape(P1(:,1),size(X)) ;
Y1 = reshape(P1(:,2),size(X)) ;
Z1 = reshape(P1(:,3),size(X)) ;
figure
hold on
surf(X,Y,Z)
surf(X1,Y1,Z1)
view(3)
Consider the polygon with vertices (0, 0), (1, 0), (7/10, 1), (1/2, 1/2), and (3/10, 1). Make a plot of this polygon in Matlab using the fill function. Rotate this polygon by an angle of 100 degrees using matrix-vector multiplication in Matlab with an appropriately chosen rotation matrix R. Make another plot of the rotated polygon using fill.
% Makes original polygon
X = [0 1 7/10 1/2 3/10];
Y = [0 0 1 1/2 1];
norm_poly = fill(X,Y,'k');
thetad = 100;
R = [cosd(thetad) -sind(thetad); sind(thetad) cosd(thetad)];
C = repmat([0 0], 5, 1)';
axis([-10 10 -10 10])
V = get(norm_poly,'Vertices')'; % get the current set of vertices
V = R*(V - C) + C; % do the rotation relative to the centre of the
square
set(norm_poly,'Vertices',V'); % update the vertices
How would I make to different plots to show them? Does the code to rotate make sense and answer all the requirements?
The rotation itself makes sense. To plot multiple things to the same graph use hold on after making the first plot. Alternatively you can make a new figure and plot a new fill there.
P = [0, 1, 7/10, 1/2, 3/10;
0, 0, 1, 1/2, 1];
fill(P(1,:), P(2,:), 'k');
theta = 100;
R = #(t) [cosd(t) -sind(t); sind(t) cosd(t)];
axis([-3 3 -3 3])
grid on
V = R(theta)*P;
hold on;
fill(V(1,:), V(2,:), 'k');
I have the following script:
close all; clear all; clc;
x = linspace(-2*pi,2*pi);
y = linspace(0,4*pi);
[X,Y] = meshgrid(x,y);
Z = sin(X)+cos(Y);
values = -10:0.5:10;
figure
[C,hh] = contour(X, Y, Z, values,'r', 'LineWidth',1);
clabel(C, hh, values, 'fontsize',7)
As you can see in the contour lines, all of the lines are plotted with LineWidth = 1. I would like to plot special line for the value = 0, with LineWidth = 2, how to set it? Thanks a lor for your help.
You will need to make a secondary contour plot to highlight the desired contour levels. The MathWorks has an example of this in the documentation.
For your case we'll have something like the following:
% Generate sample data
x = linspace(-2*pi,2*pi);
y = linspace(0,4*pi);
[X,Y] = meshgrid(x,y);
Z = sin(X)+cos(Y);
values = -10:0.5:10;
% Generate initial contour plot
figure
[C,hh] = contour(X, Y, Z, values,'r', 'LineWidth',1);
clabel(C, hh, values, 'fontsize',7)
% Generate second contour plot with desired contour level highlighted
hold on
contour(X, Y, Z, [0 0], 'b', 'LineWidth', 2);
hold off
Which returns the following:
Not that I've specified the single contour level as a vector. This is explained by the documentation for contour:
contour(Z,v) draws a contour plot of matrix Z with contour lines at the data values specified in the monotonically increasing vector v. To display a single contour line at a particular value, define v as a two-element vector with both elements equal to the desired contour level. For example, to draw contour lines at level k, use contour(Z,[k k])
If you want to highlight multiple levels then this does not apply (e.g. contour(X, Y, Z, [-1 0], 'b', 'LineWidth', 2) to highlight -1 and 0)
I want to plot an area of a graph (x = 1:24, y = -1:1) with one color (black), which I then want to decrease/increase in shading in terms of the time of day. So, that I have a plot which is 'dark' in the background at night and 'light' during the day with x being hours of day and y being a data value. My sunrise would be at 6.8 and my sunset would be at 22. I would then overlay scatter plots with data on top.
I have tried messing around with patch and area but with no luck. Here is some code I got from elsewhere on the internet but I'm not sure how to proceed:
% Define x, f(x), and M(x)
x = linspace(6.8, 22)';
f = sin(x)+cos(x);
M = x.^2;
% Define the vertices: the points at (x, f(x)) and (x, 0)
N = length(x);
verts = [x(:), f(:), x(:) zeros(N,1)];
% Define the faces to connect each adjacent f(x) and the corresponding points at y = 0.
q = (1:N-1)';
faces = [q, q+1, q+N+1, q+N];
p = patch('Faces', faces, 'Vertices', verts, 'FaceVertexCData', [M(:); M(:)], 'FaceColor', 'interp', 'EdgeColor', 'none')
So I want the end result to be similar to the image attached below (note faint shading - I would like the gradient stronger), but with x = 1:24 and y = -1:1.
Since you ignored my request to include the results of your MATLAB code in your question, I had to look into my crystal ball to determine that your "messing around" resulted in the following error message:
Error using patch
While setting the 'Vertices' property of Patch:
Value must be a 1x2 or 1x3 vector of numeric type
Error in X (line 13)
p = patch('Faces', faces, 'Vertices', verts, 'FaceVertexCData', [M(:); M(:)], 'FaceColor', 'interp', 'EdgeColor', 'none')
This can be resolved by concatenating the vertices vertically instead of horizontally in line 8:
verts = [x(:), f(:); x(:), zeros(N,1)];
This yields the following plot:
This is of course not what you want.
Actual Solution
You can build up your background from 5 rectangles:
x = [0;
6; % 'First daylight'
6.8; % Full brightness
22; % Beginning of 'sunset'
22.8; % Complete darkness
24];
vertices = [repmat(x,2,1), [ones(size(x)); -1.*ones(size(x))]];
faces = [1:5; 2:6; 8:12; 7:11].';
color = repmat([0 0 0; % 'Night' color
0 0 0;
1 1 1; % 'Day' color
1 1 1;
0 0 0;
0 0 0],2,1);
patch('Faces',faces, ...
'Vertices',vertices, ...
'FaceVertexCData',color, ...
'FaceColor','interp', ...
'EdgeColor','red');
xlim([0 24]);
You make the vertices of the 'night' rectangles black and the vertices of the 'day' rectangle white by assigning the appropriate RGB values to the FaceVertexCData property.
The color of 'sunrise'/'sunset' rectangles is then interpolated between black and white by setting the FaceColor property to 'interp'.
The EdgeColor has only been set to 'red' to illustrate how the background is constructed. Set the property to 'interp' to make the lines disappear.
So I have already implemented every part of a Hough Transform on my own, except for actually plotting the lines back onto the original image.
I can set up my array of data that I have like this.
points | theta | rho
-------|-------|----
[246,0] -90 -246
[128,0] -90 -128
[9,0] -90 -9
[0,9] 0 9
[0,128] 0 128
[0,246] 0 246
The points are the points that were converted from the peaks in Polar Coordinates. So now I need to draw all six of these lines and I have had no luck.
Edit
So I tried to change my code based off suggestions. This is what I came up with.
function help(img, outfile, peaks, rho, theta)
imshow(img);
x0 = 1;
xend = size(img,2);
peaks_len=length(peaks);
for i=1:peaks_len
peak=peaks(i,:);
r_ind=peak(1);
t_ind=peak(2);
r=rho(r_ind);
th=theta(t_ind);
%display([r,th,peak]);
%// if a vertical line, then draw a vertical line centered at x = r
% display([r, th]);
if (th == 0)
display('th=0');
display([1, size(img,1)]);
line([r r], [1 size(img,1)], 'Color', 'green');
else
%// Compute starting y coordinate
y0 = abs((-cosd(th)/sind(th))*x0 + (r / sind(th)))+11;%-25;
%// Compute ending y coordinate
yend = abs((-cosd(th)/sind(th))*xend + (r / sind(th)))+11;%-25;
display('y');
display([y0, yend]);
display('x');
display([x0 xend]);
%// Draw the line
line([x0 xend], [y0 yend], 'Color', 'green');
end
end
end
I had to change from r==0 to th==0 because th=0 would give NAN errors when r was not 0.
Based off the peaks, I then used that to get the data I needed to then calculate some values... but for some reason this isn't plotting well.
If you notice the + 11 for both y values. I had to do that to get the lines to be where they need to. I have a feeling something else went wrong.
I did change it so that my Rho values are all now positive.
If you recall from the parameterization of the Hough space, the direct relation between rho,theta to x,y is:
rho = x*cos(theta) + y*sin(theta)
Bear in mind that x,y represent the column and row location respectively. In addition, the origin is defined at the top-left corner of the image. Now that you want to plot the equation of the line, you have your rho and theta. Simply re-arrange the equation so that you can solve for an equation of the line of the form y = mx + b:
As such , simply loop over each rho and theta you have and draw a line that starts from the origin at x = 0 up to the limit of your image x = width-1. However, because MATLAB is 1-indexed, we need to go from x = 1 to x = width. Supposing that your rho and theta are stored in separate arrays of the same lengths and you have your edge image stored in im, you can do something like this:
imshow(im); %// Show the image
hold on; %// Hold so we can draw lines
numLines = numel(rho); %// or numel(theta);
%// These are constant and never change
x0 = 1;
xend = size(im,2); %// Get the width of the image
%// For each rho,theta pair...
for idx = 1 : numLines
r = rho(idx); th = theta(idx); %// Get rho and theta
%// Compute starting y coordinate
y0 = (-cosd(th)/sind(th))*x0 + (r / sind(th)); %// Note theta in degrees to respect your convention
%// Compute ending y coordinate
yend = (-cosd(th)/sind(th))*xend + (r / sind(th));
%// Draw the line
line([x0 xend], [y0 yend], 'Color', 'blue');
end
The above code is pretty simple. First, show the image using imshow in MATLAB. Next, use hold on so we can draw our lines in the image that will go on top of the image. Next, we calculate how many rho,theta pairs there are, and then we define the two x coordinates to be 1 and width as we will use these to determine where the starting and ending y coordinates are, given these x coordinates. Next, for each rho,theta pair we have, determine the corresponding y coordinates, then use line to draw a line from the starting and ending (x,y) coordinates in blue. We repeat this until we run out of lines.
Don't be alarmed if the y coordinates that are produced go out of bounds in the image. line will be intelligent enough to simply cap the result.
When theta = 0
The above code works assuming that you have no vertical lines detected in the Hough Transform, or when theta = 0. If theta = 0 (like in your case), this means that we have a vertical line which would thus produce an infinite slope and our formulation of y = mx + b is invalid. Should theta = 0, the equation of the line becomes x = rho. As such, you will need an additional if statement inside your loop that will detect this:
imshow(im); %// Show the image
hold on; %// Hold so we can draw lines
numLines = numel(rho); %// or numel(theta);
%// These are constant and never change
x0 = 1;
xend = size(im,2); %// Get the width of the image
%// For each rho,theta pair...
for idx = 1 : numLines
r = rho(idx); th = theta(idx); %// Get rho and theta
%// if a vertical line, then draw a vertical line centered at x = r
if (th == 0)
line([r r], [1 size(im,1)], 'Color', 'blue');
else
%// Compute starting y coordinate
y0 = (-cosd(th)/sind(th))*x0 + (r / sind(th)); %// Note theta in degrees to respect your convention
%// Compute ending y coordinate
yend = (-cosd(th)/sind(th))*xend + (r / sind(th));
%// Draw the line
line([x0 xend], [y0 yend], 'Color', 'blue');
end
end
In order to draw the vertical line, I need to know how high the image is so that we can draw a vertical line from the top of the image (y = 1) down to the bottom of the image (y = height) which is anchored at x = rho. As such, the above code should now properly handle any line, as well as the degenerate case when the slope is infinite. Therefore, this second version of the code is what you're after.
Good luck!