Scatter doesn't work properly Matlab - matlab

I wrote a code to create a movie of moving points. I used scatter to make points colored( color of points depends to the density of points of the area). x and y are position of points and I have put below code in a for loop for passing time:
figure(h1); % set figure 1 as current figure
cla
dens = zeros(size(x));
% Tolerence is distance betweeen points (squared)
tol = 1;
for ii = 1:numel(x)
% Loop through all points, count neighbours within tolerence
dens(ii) = sum((x - x(ii)).^2 + (y - y(ii)).^2 < tol);
end
% Normalise density onto range 0-1
dens = (dens/max(dens));
hold on
scatter(x,y, [], dens,'filled','d')
drawnow
currFrame = getframe;
writeVideo(vidObj,currFrame);
When I run the code, the movie is created but all points are green. Could anyone guess what is the reason and how to solve it?

Your dens calculation is dependent on the relation between the values of x and y, and the value of tol. If tol is too large, then all the points a counted as "in the region" of all other points, if it's too small, then all points are "alone". In both cases, dens has the same values for all points and scatter color them all in green.

Related

Creating a circle in a square grid

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.

How to plot 2D advection problem when using meshgrid to find value for velocity?

I am currently trying to solve a 2nd order 2D advection equation using the upwind scheme. At first the task is to plot a quiver() plot and then over lay it on top of a contourf(). When using the data for velocity u and v into the upwind scheme I am getting straight line outputs as seen below. However I am using an initial condition of phi0 = cos(x). When checking the values for X,Y,u,v they all have different values at (i,j) locations. I see that my phi0 and phi are staying constant down each column but the output of the advection should be different at different times. I followed my 1D 2nd order code which works perfectly, but cannot seem to get a advected moving graph. Any advice on my setup or if you can point out where I am going wrong with this plot would be a big help!
clear all
clc
%Problem 1
%Part B
%Creating a quiver plot for the 2D vector profile
L=2*pi;%Length Lx=Ly = 2pi
L0=0;
N=31; % Nx=Ny=31
%get a value of dx=dy to know distance between steps
dx=L/N;
dy=L/N
x=L0-dx*2:dx:L+dx*2;
y=L0-dy*2:dy:L+dy*2;
[X,Y]=meshgrid(x,y);
u=cos(X).*sin(Y);
v=-sin(X).*cos(Y);
figure
hold on
%Part B & C using the courtf plot Phi=cos(x)
phi0=cos(X);
contourf(X,Y,phi0)
colormap autumn
colorbar
xlabel('Length from 0 to 2*pi with dx spacing')
ylabel('Length from 0 to 2*pi with dy spacing')
title('Quiver plot on the phi=cos(x) Contour Plot')
quiver(X,Y,u,v)
hold off
figure(2)
plot(x,phi0)
%%
%Writing a code to sovle 2D advection for part D
t=0; %initial time
tmax=10; %Maximum time
dt=0.01; %time step
phi=phi0;
phip1=phi0;
%phi(:)=phi; %Initial Condition
nsteps = tmax/dt
%Add periodic boundary conditions for both x & y direction
for n=1 : nsteps
phi(1,:) = phi(end-2,:);
phi(2,:) = phi(end-3,:);
phi(end,:) = phi(3,:);
phi(end-1,:) = phi(4,:);
%Y ghost cells
phi(:,1) = phi(:,end-2);
phi(:,1) = phi(:,end-3);
phi(:,end) = phi(:,3);
phi(:,end-1) = phi(:,4);
for i=3:N+1
for j=3:N+1
if u > 0 & v>0
phip1(i,j)= phi(i,j) - u(i)*dt/(2*dx) * (3*phi(i,j)-4*phi(i-1,j)+phi(i-2,j))- v(j)*dt/(2*dx) *(3*phi(i,j)-4*phi(i,j-1)+phi(i,j-2))
elseif u <0 & v<0
phip1(i,j)= phi(i,j) - u(i)*dt/(2*dx) * (-3*phi(i,j)+4*phi(i+1,j)-phi(i+2,j))- v(j)*dt/(2*dx) *(-3*phi(i,j)+4*phi(i,j+1)-phi(i,j+2))
elseif u >0 & v <0
phip1(i,j)= phi(i,j) - u(i)*dt/(2*dx) * (3*phi(i,j)-4*phi(i+1,j)+phi(i+2,j))- v(j)*dt/(2*dx) *(-3*phi(i,j)+4*phi(i,j-1)-phi(i,j-2))
elseif u <0 & v >0
phip1(i,j)= phi(i,j) - u(i)*dt/(2*dx) * (-3*phi(i,j)+4*phi(i-1,j)-phi(i-2,j))- v(j)*dt/(2*dx) *(3*phi(i,j)-4*phi(i,j+1)+phi(i,j+2))
end
end
end
t=t+dt;
phi=phip1;
plot(x,phi)
%pause(0.5)
end
The problem I see is that you are plotting phi (which is 2D) against x (which is 1D).
I am not 100% sure on the right section you want to plot but something along the lines of this should work: plot(x,phi0(1,:)). What this does is to plot the first slice of phi in the y direction.
EDIT
To visualize phi0 as function of both X and Y, you can use either surf(X,Y,phi0) or mesh(X,Y,phi0).

Creating histograms of distance from the origin for 2D Random Walkers

Let's say you can show the distribution in space of the the positions of a large number of random walkers at three different time points. This was provided an answer to my previous question and with some tweaks is beautiful.
clc;
close all;
M = 1000; % The amount of random walks.
steps = [100,200,300]; % here we analyse the step 10,200 and 1000
cc = hsv(length(steps)); % manage the color of the plot
%generation of each random walk
x = sign(randn(max(steps),M));
y = sign(randn(max(steps),M));
xs = cumsum(x);
xval = xs(steps,:);
ys = cumsum(y);
yval = ys(steps,:);
hold on
for n=1:length(steps)
plot(xval(n,:),yval(n,:),'o','markersize',1,'color',cc(n,:),'MarkerFaceColor',cc(n,:));
end
legend('100','200','300')
axis square
grid on;
Now to the question, could I in some way use the hist() and subplot() functions to show the distance from the origin of the random walkers at three separate time points, or more I guess, but three for simplicity.
I'm not sure how to go about this beyond producing distributions of random walkers at the three time points themselves so far.
I hope that I've understand your question, I think that you want to use the bar plot with the stack option.
I've used the answer of #LuisMendo to my question to increase the code efficiency.
steps = [10,200,1000]; % the steps
M = 5000; % Number of random walk
DV = [-1 1]; % Discrete value
p = .5; % probability of DV(2)
% Using the #LuisMendo binomial solution:
for ii = 1:length(steps)
xval(ii,:) = (DV(2)-DV(1))*binornd(steps(ii), p, M, 1)+DV(1)*steps(ii);
yval(ii,:) = (DV(2)-DV(1))*binornd(steps(ii), p, M, 1)+DV(1)*steps(ii);
end
[x, cen] = hist(sqrt(xval.^2+yval.^2).'); %where `sqrt(xval.^2+yval.^2)` is the euclidian distance
bar(cen,x,'stacked');
legend('10','200','1000')
axis square
grid on;
Increase the # of bins in the histogram function to increase the plot precision.
Results:

Specifying quiver vector color by density

I have some vectors with defined position and orientation. I could show them in space by using the below code:
theta = [pi/2,-pi/2,pi/2,pi/2,pi/2,pi/2,pi/2];
r = 0.25; % magnitude (length) of arrow to plot
x = [4,3.5,3.75,4.5,8,10,12]; y = [8.5,8.2,8.3,8,9,10,8];
u = r * cos(theta); % convert polar (theta,r) to cartesian
v = r * sin(theta);
h = quiver(x,y,u,v,'linewidth',2);
set(gca, 'XLim', [2 15], 'YLim', [4 15]);
As is clear from the image, in some regions the number of arrows is more than in other places. I want to show the arrows by color, where each color represents the density of the arrows.
Could anyone help me to do that? It would also be a good solution if there is a continuous background color which shows local densities.
Edit: Below are some options for colouring the background of the plot depending on the density of your points. I'm editing this into the top of my answer because it actually answers your question - individually colouring quiver arrows based on density!
x = rand(200,1)*10; y = rand(200,1)*10; % Set up random points
r = 1; u = r * cos(x); v = r * sin(y); % Quiver directions
colormap winter; c = colormap; % Set colourmap and assign to matrix
% Get density of points broken into a 10x10 grid
[n,~,~,binX,binY] = histcounts2(x,y,[10,10]);
% Get colour based on histogram density and chosen colormap colours
col = c(ceil(n(sub2ind(size(n), binX, binY))/max(n(:))*size(c,1)),:);
figure; hold on;
% Each quiver point must be plotted individually (slow!) because colours can
% only be applied to individual quivers. This could be sped up by plotting
% all of the same colour at once.
for ii = 1:size(x,1);
quiver(x(ii),y(ii),u(ii),v(ii),0,'color',col(ii,:));
end
Output:
Note: unlike the below example, you cannot use hist3 because you need it to return the bin index too. You could try this File Exchange function to achieve the same result (untested).
Here is an option using hist3 to get the density (in this example I use a 10x10 grid, as specified when calling hist3). Then using pcolor to display the density, and shading interp to smooth the colours.
Note: hist3 requires the Stats & ML toolbox, if you have Matlab 2015b or newer you can instead use the standard function histcounts2(x,y).
% Generate points and quiver directions
x = rand(200,1)*10; y = rand(200,1)*10;
u = r * cos(x); v = r * sin(y);
% Get density of points, format for input to pcolor
n = hist3([x,y],[10,10]); % Get density of points broken into a 10x10 grid
colx = linspace(min(x),max(x),size(n,1)+1);
coly = linspace(min(y),max(y),size(n,1)+1);
n = n'; n(size(n,2)+1,size(n,1)+1) = 0;
% Plot
figure
pcolor(colx,coly,n) % Density plot
hold on; colorbar; % Hold on for next plot and show colour bar key
quiver(x,y,u,v,'r') % Quiver plot
shading interp % Smooth plot colours
Output:
Edit: making the colours more muted
You can control the colours using colormap. This could be one of the defaults, or you can create a custom map of RGB triplets and have whatever colours you want! Here is an example, simply calling colormap bone; at the end of the above code:
In a custom colour map, you could make the colours even more muted / less contrasting.
Additionally, you can use caxis to scale the colour axis of a plot! Simply call
caxis([0,2*max(n(:))]);
at the end of the above code to double the maximum colour map value. You can tweak the 2 to get desired results:
this looks way less fancy but specifies the arrow color as function of the number of arrows in a certain number of bins of the x-axis
close all;
cm=colormap;
theta = [pi/2,-pi/2,pi/2,pi/2,pi/2,pi/2,pi/2];
r = 0.25; % magnitude (length) of arrow to plot
x = [4,3.5,3.75,4.5,8,10,12]; y = [8.5,8.2,8.3,8,9,10,8];
[n,c]=hist(x,5); %count arroes in bins
u = r * cos(theta); % convert polar (theta,r) to cartesian
v = r * sin(theta);
figure;hold on
for ii=1:numel(n) %quiver bin by bin
if n(ii)>0
if ii==1
wx=find(x<(c(ii)+(c(ii+1) - c(ii))/2)); %Which X to plot
elseif ii==numel(n)
wx=find(x>c(numel(n)-1));
else
wx=find((x>(c(ii)-(c(ii)-c(ii-1))/2)).*(x<(c(ii+1)-(c(ii+1)-c(ii))/2)));
end
indCol=ceil( (size(cm,1)*n(ii)-0) / max(n));%color propto density of arrows %in this bin
col = cm(indCol,:);%color for this bin
h = quiver(x(wx),y(wx),u(wx),v(wx),0,'linewidth',2,'color',col);
end
end
colorbar
caxis([0 max(n)])

matlab: moving circle along a graph and axis equal

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).