Hello and pardon me if my english is a bit rusty. I'm trying to create a circle that moves along a parametric function (coordinates are stored in vectors). I have written a function for drawing the circle and I know that you can use the axis equal command in matlab in order to create a circle shape and avoid an ellipse. My problem is that when I do this the figure window becomes very wide relative to the plotted graph. Any input is appreciated.
MAIN CODE:
t = linspace(0,3);
x = 30*cos(pi/4)/2*(1-exp(-0.5*t));
y = (30*sin(pi/4)/2 + 9.81/0.5^2)*(1-exp(0.5*t)) - 9.81*t/0.5;
for i = 1:length(t)
plot(x,y)
axis equal
hold on
cirkel(x(i),y(i),1,1,'r') % argument #3 is the radius #4 is 1 for fill
hold off
pause(0.01)
end
CIRCLE CODE:
function cirkel(x,y,r,f,c)
angle = linspace(0, 2*pi, 360);
xp = x + r*cos(angle);
yp = y + r*sin(angle);
plot(x,y)
if f == 1 && nargin == 5
fill(xp,yp,c)
end
When you call axis equal it makes one unit of the x axis be the same size as one unit of the y axis. You are seeing what you are because your y values span a much larger range than the x values.
One way to deal with this would be to query the aspect ratio and x/y limits of the current axes as shown in the second part of this answer. However, an easier approach is rather than using fill to plot your circle, you could instead use scatter with a circular marker which will be circular regardless of the aspect ratio of your axes.
t = linspace(0,3);
x = 30*cos(pi/4)/2*(1-exp(-0.5*t));
y = (30*sin(pi/4)/2 + 9.81/0.5^2)*(1-exp(0.5*t)) - 9.81*t/0.5;
% Plot the entire curve
hplot = plot(x, y);
hold on;
% Create a scatter plot where the area of the marker is 50. Store the handle to the plot
% in the variable hscatter so we can update the position inside of the loop
hscatter = scatter(x(1), y(1), 50, 'r', 'MarkerFaceColor', 'r');
for k = 1:length(t)
% Update the location of the scatter plot
set(hscatter, 'XData', x(k), ... % Set the X Position of the circle to x(k)
'YData', y(k)) % Set the Y Position of the circle to y(k)
% Refresh the plot
drawnow
end
As a side note, it is best to update existing plot objects rather than creating new ones.
If you want the small dot to appear circular, and you want to have a reasonable domain (x-axis extent), try this:
function cirkel(x,y,r,f,c)
angle = linspace(0, 2*pi, 360);
xp = x + 0.04*r*cos(angle); %% adding scale factor of 0.04 to make it appear circular
yp = y + r*sin(angle);
plot(x,y)
if f == 1 && nargin == 5
fill(xp,yp,c)
end
Note the addition of the scale factor in the computation of xp. If you want to automate this, you can add another parameter to cirkel(), let's call it s, that contains the scale factor. You can calculate the scale factor in your script by computing the ratio of the range to the domain (y extent divided by x extent).
Related
I try to solve the following 2D elliptic PDE electrostatic problem by fixing the Parallel plate Capacitors code. But I have problem to plot the circle region. How can I plot a circle region rather than the square?
% I use following two lines to label the 50V and 100V squares
% (but it should be two circles)
V(pp1-r_circle_small:pp1+r_circle_small,pp1-r_circle_small:pp1+r_circle_small) = 50;
V(pp2-r_circle_big:pp2+r_circle_big,pp2-r_circle_big:pp2+r_circle_big) = 100;
% Contour Display for electric potential
figure(1)
contour_range_V = -101:0.5:101;
contour(x,y,V,contour_range_V,'linewidth',0.5);
axis([min(x) max(x) min(y) max(y)]);
colorbar('location','eastoutside','fontsize',10);
xlabel('x-axis in meters','fontsize',10);
ylabel('y-axis in meters','fontsize',10);
title('Electric Potential distribution, V(x,y) in volts','fontsize',14);
h1=gca;
set(h1,'fontsize',10);
fh1 = figure(1);
set(fh1, 'color', 'white')
% Contour Display for electric field
figure(2)
contour_range_E = -20:0.05:20;
contour(x,y,E,contour_range_E,'linewidth',0.5);
axis([min(x) max(x) min(y) max(y)]);
colorbar('location','eastoutside','fontsize',10);
xlabel('x-axis in meters','fontsize',10);
ylabel('y-axis in meters','fontsize',10);
title('Electric field distribution, E (x,y) in V/m','fontsize',14);
h2=gca;
set(h2,'fontsize',10);
fh2 = figure(2);
set(fh2, 'color', 'white')
You're creating a square due to the way you're indexing (see this post on indexing). You've specified the rows to run from pp1-r_circle_small to pp1+r_circle_small and similar for the columns. Given that Swiss cheese is not an option, you're creating a complete square.
From geometry we know that all points within distance sqrt((X-X0)^2 - (Y-Y0)^2) < R from the centre of the circle at (X0,Y0) with radius R are within the circle, and the rest outside. This means that you can simply build a mask:
% Set up your grid
Xsize = 30; % Your case: 1
Ysize = 30; % Your case: 1
step = 1; % Amount of gridpoints; use 0.001 or something
% Build indexing grid for circle search, adapt as necessary
X = 0:step:Xsize;
Y = 0:step:Ysize;
[XX,YY] = meshgrid(X, Y);
V = zeros(numel(X), numel(Y));
% Repeat the below for both circles
R = 10; % Radius of your circle; your case 0.1 and 0.15
X0 = 11; % X coordinate of the circle's origin; your case 0.3 and 0.7
Y0 = 15; % Y coordinate of the circle's origin; your case 0.3 and 0.7
% Logical check to see whether a point is inside or outside
mask = sqrt( (XX - X0).^2 + (YY - Y0).^2) < R;
V(mask) = 50; % Give your circle the desired value
imagesc(V) % Just to show you the result
axis equal % Use equal axis to have no image distortion
mask is a logical matrix containing 1 where points are within your circle and 0 where points are outside. You can then use this mask to logically index your potential grid V to set it to the desired value.
Note: This will, obviously, not create a perfect circle, given you cannot plot a perfect circle on a square grid. The finer the grid, the more circle-like your "circle" will be. This shows the result with step = 0.01
Note 2: You'll need to tweek your definition of X, Y, X0, Y0 and R to match your values.
I'm trying to create a gradient fill inside a circular area according to a given function. I hope the plot below explains it at best
I'm not sure how to approach this, as in the simulation I'm working on the direction of the gradient changes (not always in the x direction as below, but free to be along all the defined angles), so I'm looking for a solution that will be flexible in that manner as well.
The code I have is below
clear t
N=10;
for i=0:N
t(i+1) = 0+(2*i*pi) / N;
end
F = exp(-cos(t))./(2.*pi*besseli(1,1));
figure(1)
subplot(1,3,1)
plot(t*180/pi,F,'-ob')
xlim([0 360])
xlabel('angle')
subplot(1,3,2)
hold on
plot(cos([t 2*pi]), sin([t 2*pi]),'-k','linewidth',2);
plot(cos([t 2*pi]), sin([t 2*pi]),'ob');
plot(cos(t).*F,sin(t).*F,'b','linewidth',2);
subplot(1,3,3)
hold on
plot(cos([t 2*pi]), sin([t 2*pi]),'-k','linewidth',2);
plot(cos([t 2*pi]), sin([t 2*pi]),'ob');
To fill surface, you need to use the patch command.
t = linspace(0, 2*pi, 100);
x = cos(t);
y = sin(t);
c = x; % colored following x value and current colormap
figure
patch(x,y,c)
hold on
scatter(x,y)
hold off
colorbar
Resulting graph:
Colors are defined in c per point, and are interpolated inside the shape, so I'm sure that you should have all freedom to color as you want!
For example, the rotated version:
t = linspace(0, 2*pi, 100);
x = cos(t);
y = sin(t);
c = cos(t+pi/4)
figure
patch(x,y,c)
colorbar
To understand how it is going on, just think that every point has a color, and matlab interpolate inside. So here I just rotated the intensity per point by pi /4.
For this to work you need to have a filled shape, and you may need to customize the color (c) parameter so that it matches your need. For example, if your gradient direction is encoded in a vector, you want to project all your point onto that vector to get the value along the gradient for all points.
For example:
% v controls the direction of the gradient
v = [0.1, 1];
t = linspace(0, 2*pi, 100);
F = exp(-cos(t))./(2.*pi*besseli(1,1));
% reconstructing point coordinate all around the surface
% this closes the path so with enough points so that interpolation works correctly
pts = [[t', F']; [t(end:-1:1)', ones(size(t'))*min(F)]];
% projecting all points on the vector to get the color
c = pts * (v');
clf
patch(pts(:,1),pts(:,2),c)
hold on
scatter(t, F)
hold off
I am using MATLAB to print my simulation results. The results concerns a UAV's trajectory and waypoints that the UAV has to visit. The UAV is supposed to be equipped with a camera, whose range view is 10x10. Right now, the diagram shows the UAV's trajectory as a line visiting the waypoints. Is it possible, to show the camera's footprint, instead of the actual trajectory? I would like it to plot the rectangular camera's view to show the exhaustive coverage of the area. There is the option to plot the points as square, or cross, or cyrcles, but is it possible to set the boundaries of those?
Thank you in advance
The problem with using the marker size to indicate the range view is that there is no direct relation between the data units of your waypoints and the marker size. In other words, a value of 10 for the marker size doesn't necessarily mean that a side of a square marker is going to be 10 data units long (as defined by the scaling and limits of the axes).
An alternative is to plot square patches at each of your waypoints where the patch is aligned with the trajectory of the UAV. Here's how you can do this:
% Generate some sample data:
N = 20; % Number of waypoints
x = cumsum(5.*rand(1, N)); % X coordinates of UAV
y = cumsum(5.*rand(1, N)); % Y coordinates of UAV
% Compute vectors parallel and perpendicular to the trajectory at each point:
v = [diff(x); diff(y); zeros(1, N-1)]; % Vectors (1 per column)
v = bsxfun(#rdivide, v, sqrt(sum(v.^2, 1))); % Normalize each column to a unit vector
v = v(:, [1 1:end]); % Replicate a vector for starting point
vCross = cross(v, [zeros(2, N); ones(1, N)]); % Perpendicular vector
% Generate patch coordinates:
R = 10; % Range view
xPatch = [x+(R/2).*(v(1, :)+vCross(1, :)); ...
x+(R/2).*(v(1, :)-vCross(1, :)); ...
x-(R/2).*(v(1, :)+vCross(1, :)); ...
x-(R/2).*(v(1, :)-vCross(1, :))];
yPatch = [y+(R/2).*(v(2, :)+vCross(2, :)); ...
y+(R/2).*(v(2, :)-vCross(2, :)); ...
y-(R/2).*(v(2, :)+vCross(2, :)); ...
y-(R/2).*(v(2, :)-vCross(2, :))];
% Plot the patches and trajectory:
patch(xPatch, yPatch, [0 0.3 0], 'FaceAlpha', 0.25, 'EdgeColor', 'none');
hold on;
plot(x, y, '-', 'Color', [0.8 0 0], 'Marker', '.', 'MarkerSize', 12);
axis equal;
And here's a sample plot:
As a first attempt you can specify marker shape as square and set constant marker size, e.g.
plot(x,y,'s','markersize',10)
Here x and y are the vectors, holding the UAV coordinates. The letter 's' sets marker shape as square, and size is set to 10.
In reality, UAV trajectory is defined in a 3d space, where varying height above the ground corresponds to varying footprint size and shape. Taking this into account would require a bit more effort.
Also this assumes that the points are spaced closely enough otherwise there would be empty areas between markers.
I'm plotting an R^2 to R function in MATLAB as a surface plot, which I colormap and view from above.
surf(X, Y, data);
colormap(jet);
colobar;
view(2);
It produces (with some additional code) something like
though the true nature of the function (for the purpose of understanding this question) is better observed from an angle like:
I want to plot a circle atop my original plot (seen from above). Something like...
I can't seem to achieve this however, since plotting in-a-plane elements on plots makes them appear on the x-y axis, which is covered by my surface plot. For example, calling
circle_pos = [ +1 +1; -1 -1; -1 +1; +1 -1;]
circle_rad = 0.2 * ones(4,1);
viscircles(circle_pos, circle_rad);
after my surface plot results in no visible circles when viewed from the top. Zooming and rotating reveals these circles were plotted on the x-y plane, and so are invisible from above.
How do I plot my circles on top of the surface plot, so that they are visible from above?
A similar issue arises when plotting text atop the surface, but is remedied by specifying a z position value just above the underlying functions z value. There doesn't seem to be any way to specify the z position of these graphical elements.
There may not be a direct way to specify the z position of the objects returned by viscircles, but in general there is (most of the time) a way to modify properties and position of any graphic object afterwards.
Method 1: modifying circles after creation.
If you plan to do modifications of a graphic object, the first thing to do is always to retrieve its handle. So in your case, you would have to call viscircles by specifying a return value (which will contain the handle you want).:
hg = viscircles(circle_pos, circle_rad);
I do not have the Image Processing Toolbox so I do not have access to the viscircles function. However I read from the documentation that the handle returned is an hggroup. An hggroup is simply a container containing one or more handles of more primitive graphic objects. In this case the hggroup contains the handles of 4 lines (your 4 circles).
The easiest way to transform all the objects in an hggroup is to use a hgtransform object. We will define a Translation transformation and the hgtransform will apply it to the 4 circles (all the children of the hggroup).
To define the translation, we will use a makehgtform object.
Here we go:
ht = hgtransform ; % create the transform object
set(hg,'Parent',ht) ; % make it a "parent" of the hggroup
zc = max(max(Z)) ; % Find by how much we want to translate the circles on the Z axis
Tz = makehgtform('translate',[0 0 zc]) ; % create the TRANSLATION transform
set(ht,'Matrix',Tz) % apply the transformation (translation) to the hggroup/hgtransform
Done, your 4 circles should now be on top of your surface. Note that you can specify any other values for zc (not only the max of the surface).
Method 2: DIY
In case you do not want to be reliant on the image processing toolbox, or if you do not have it at all, it is relatively easy to create circles in a 3D space by yourself.
Here is a function which will create circles in a way comparable to viscircles but it also let you specify an optional z coordinate for the circle centre positions.
code for circles_3D.m:
function hg = circles_3d( pos , rad , varargin )
% get current axes handle and hold state
ax = gca ;
holdState = get(ax,'NextPlot') ; % save state to reinstate after function
set(ax,'NextPlot','add') ; % equivalent of "hold off"
tt = linspace(0,2*pi) ;
hg = hggroup(ax) ;
for k = 1:numel(rad)
c = pos(k,:) ;
r = rad(k) ;
x = c(1) + r.*cos(tt) ;
y = c(2) + r.*sin(tt) ;
z = zeros(size(x)) ;
if numel(c)==3 ; z = z + c(3) ; end
plot3(hg,x,y,z,varargin{:}) ;
end
set(ax,'NextPlot',holdState) ; % restore axes hold state
You can now call this function instead of viscircles. I used the varargin parameter to transfer any line property to the circles created (so you can specify the Color, LineWidth, and any other typical parameter you like.
For the sake of an example, I need to recreate a surface comparable to your, with 4x "zero" poles distributed around the maxima:
pc = 0.5 ; % pole centers
pw = 0.05 ; % pole widths
% surface definition
[X,Y] = meshgrid(-5:.1:5);
R = sqrt(X.^2 + Y.^2) + eps ;
Z = sin(R)./R;
% zero surface values around the defined poles
[idxPoles] = find(abs(X)>=pc-pw & abs(X)<=pc+pw & abs(Y)>=pc-pw & abs(Y)<=pc+pw ) ;
Z(idxPoles)= 0 ;
% display
hs = surf(X,Y,Z) ; shading interp
Which produces:
Now you can simply get your circles with the circles_3D function:
zc = max(max(Z)) ;
circle_pos = [ pc pc zc ; -pc -pc zc ; -pc +pc zc ; +pc -pc zc ] ;
circle_rad = 0.2 * ones(4,1);
h = circles_3d( circle_pos , circle_rad , 'Color','r','LineWidth',2) ;
and get:
Note that I made this function so it also return an hggroup object containing your lines (circles). So if you want to move them later, apply the same trick than in the first part of the answer.
Several options spring to mind.
The simplest will be to plot a marker in 3d using plot3:
figure;
peaks;
shading interp;
hold;
x = 0; y = 2; z = 10;
plot3(x, y, z, 'ro', 'MarkerSize', 24);
That will work, but the circle will always appear to be facing the viewer:
Alternatively, you can plot a circle in 3d:
vfTheta = linspace(0, 2*pi, 300);
figure; peaks; shading interp; hold;
x = 0; y = 2; z = 10; r = 0.2;
plot3(x + r.*cos(vfTheta), y + r.*sin(vfTheta), z .* ones(size(vfTheta)), 'r-', 'LineWidth', 2);
The result: a nice halo in 3d!
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!