I am trying to simulate the trajectories of a few particles in 2D on Matlab. I have the x- and y- coordinates of these particles as a function of time, which I store as matrix x and y. The column in both x and y corresponds to the time, while the row corresponds to the particle number: 1, 2, etc.
I know how to do the animation for one particle with pause, but I am not sure how to customize the code for multiple particles' trajectories. Basically, my idea is that on the initial plot, I have 3 markers which correspond to the initial position of the particles, say particle A, B and C. Then, I would like to follow the movement of these 3 markers, and here is where I encountered the problem: I don't know how to sort the subsequent points according to the particle identity. For example, I want to specify the first point I plot in the second time point as particle A, second point as particle B and third point in particle C.
I have done something similar to this, but in my simulation, the number of particles may be 100, which makes it impractical to create x1, x2, ..., x100, y1, y2, ..., y100 for the animation to work:
y = rand(3, 20); % Generate random sample data.
x = rand(size(y, 1), size(y, 2));
% Now we have x and y sample data and we can begin.
% Extract into separate arrays
x1 = sort(x(1,:));
x2 = sort(x(2,:));
x3 = sort(x(3,:));
y1 = y(1,:);
y2 = y(2,:);
y3 = y(3,:);
for k = 1 : length(x1)
plot(x1(1:k), y1(1:k), 'r*-', 'LineWidth', 2);
xlim([min(x(:)), max(x(:))]);
ylim([min(y(:)), max(y(:))]);
grid on;
hold on;
plot(x2(1:k), y2(1:k), 'g*-', 'LineWidth', 2);
plot(x3(1:k), y3(1:k), 'b*-', 'LineWidth', 2);
hold off;
fprintf('Plotted points 1 through %d\n', k);
pause(0.8);
end
Any ideas or suggestions will be greatly appreciated!
In order to plot all graphs at once, we might make an 2D array.
Below is an example.
y = rand(3, 20); % Generate random sample data.
x = rand(size(y, 1), size(y, 2));
% Now we have x and y sample data and we can begin.
% Extract into separate arrays
x = sort(x');
y=y';
M=size(x);
N=M(2);
for k = 1 : length(x)
if k==1;
zeroPad=zeros(1,N);
x0=[zeroPad;x(1,1:N)];
y0=[zeroPad;y(1,1:N)];
plot(x0(1:2,1:N), y0(1:2,1:N), '*', 'LineWidth', 2);
else
plot(x(1:k,1:N), y(1:k,1:N), '*-', 'LineWidth', 2);
end
xlim([min(x(:)), max(x(:))]);
ylim([min(y(:)), max(y(:))]);
grid on;
fprintf('Plotted points 1 through %d\n', k);
pause(0.8);
end
One trick was added.
At the first iteration, I added zeros before x and y.
Some unnecessary codes were removed.
Related
I need to shade the area in Matlab plots, above the threshold, the small area as highlighted, under the blue curve (DC) but outside red one (PV), above the threshold; th=120
load('LP_DCHEMS_PV.mat','DC'); % DCCHEMS loaded
load('FifteenPercentHotDay.mat','PV') % Hot Day Case I, 15%
th=120;
plot(DC);
hold on
plot(PV);
yline(th);
When I use the following commands, it shades the area as shown in fig 2 attached. something has to be corrected in this line : h2=area([y1(:) , (PV(:)-DC(:)).* (DC(:)>th )]);
If anyone can please guide,
y1=zeros(1,144);
y1(1,:)=th;
x=1:144;
h2=area([y1(:) , (PV(:)-DC(:)).* (DC(:)>th )]);
h2(1).FaceColor=[1 1 1]; % white
h2(2).FaceColor=[0 0 1 ]; % blue
hold on;
plot(x,DC,'b',x,PV,'r');
title('overlapped area ');
xlim([1 144]);
Consider the script below:
clear; close all; clc;
% Generate dummy data
x = 1:150;
y1 = exp(-(x(:)-35).^2/750)+1.4*exp(-(x(:)-100).^2/1000)+0.1*rand(150,1);
y2 = -1e-3*(x(:)-75).^2 + 1.85 + 0.1*rand(150,1); y2(y2<0) = 0;
% Set some threshold
threshold = 0.85;
% Create the plot
fig = figure(1); hold on; box on;
plot(x, y1, 'LineWidth', 1.5);
plot(x, y2, 'LineWidth', 1.5);
plot([x(1) x(end)], threshold*ones(1,2), 'k:', 'LineWidth', 1.5);
% Mark the areas and customise them (by changing FaceAlpha, as an example)
areas = MarkAreasInPlot(x, y1, y2, threshold, 'g');
for i = 1:length(areas)
areas{i}.FaceAlpha = 0.5;
end
legend('y_1', 'y_2', 'threshold', 'Location', 'NorthWest');
At the top, I generate some dummy data. The generation of the y1 values is loosely based on what bla did in this answer, while y2 is simply a parabolic function (with some distortion) where all values below zero are filtered out. The next thing we do is define some threshold, which indicates the lowest possible boundary. After this, we simply plot the data and the threshold.
Then, I call the MarkAreasInPlot function:
function areas = MarkAreasInPlot(x, y1, y2, threshold, color)
% This function fills the areas for which the following two statements
% are true:
% (1) y1 > y2
% (2) y1 > threshold
% Reshape arrays (to column vectors) if necessary
x = reshape(x, 1, []);
y1 = reshape(y1, 1, []);
y2 = reshape(y2, 1, []);
% Create filter and find indices
filter = (y1 > y2) & (y1 > threshold);
idx = find(filter==1);
% If nothing was found, return empty array
if isempty(idx)
areas = {};
return;
end
% Determine the number of areas by looping over the indices (idx) and
% checking if the next index follows up the previous one. If this is
% not the case, it means that we are dealing with separate areas
num_areas = 0;
idx_areas = idx(1);
for i = 1:length(idx)-1
if(idx(i+1) ~= idx(i)+1)
num_areas = num_areas + 1;
idx_areas = [idx_areas, idx(i), idx(i+1)];
end
end
idx_areas = [idx_areas, idx(end)];
% Draw all areas
areas = cell(num_areas+1, 1);
for i = 1:2:2*(num_areas+1)
% Determine span of indices for area
idx_span = idx_areas(i):idx_areas(i+1);
% Determine x- and y-coordinates
x_area = x(idx_span);
x_area = [x_area, flip(x_area)];
y_top = y1(idx_span);
y_bot = y2(idx_span);
y_bot(y_bot < threshold) = threshold;
y_area = [y_top, flip(y_bot)];
% Fill the area
areas{i} = fill(x_area, y_area, color, 'EdgeColor', 'None');
end
end
This function works as follows. First, we make sure that all data arrays (x, y1 and y2) are column vectors (this is to prevent possible issues in case they are row vectors). Subsequently, we determine at which indices of the data arrays y1 > y2 and y1 > threshold. Note that filter is a logical array where an element is 1 only when the aforementioned two statements are true. Then, we determine the indices of the elements in filter were we have a value of 1. At these indices of the data arrays, we know that the two statements are true. Of course, we check for the case that the statements are false at all positions, in which case we don't draw any areas and return an empty cell array. After finding the indices, we loop over them and check for a case were the element at the next index is not equal to the value at the current index plus 1. If we find such a case, we now that we are dealing with two separate areas. Any time this happens, we store the two values at these two indices in idx_areas and increase the counter for the number of areas (num_areas) by 1. After doing this, idx_areas contains an even amount of elements. We know that we need to fill the area between y1 and max(y2, threshold) at the indices given by the range idx_areas(n):idx_areas(n+1) for n = 1, 3, 5, 7, ... (the values for n of course depending on the value of num_areas). This is exactly what we do at the end of the function, using the fill function. Note that we use a for loop over the number of areas we should fill and store the handle for each fill call in the areas cell array, which is returned.
If I run the script at the top, I get the following output:
As you can see, it properly detects and fills the areas. However, due to the discrete nature of the data, it can happen that there is a small part at the side of an area that ends up not being completely filled:
This is not an error! Instead, this is due to the fact that the next data point for y1 is located below the threshold, meaning that the second requirement for the MarkAreasInPlot function does not hold! You could try to 'solve' this using an interpolation procedure (to find at what x-value the y1-value is exactly equal to the threshold), but I think it is not worth the effort (as it is only a very small piece that is not filled).
To show you some more results, I now set threshold to 1.6 such that no area will be detected:
And as a final example, I swap y1 and y2 in the call to MarkAreasInPlot:
areas = MarkAreasInPlot(x, y2, y1, threshold, 'g');
As expected, it now fills the area where y2 > y1 and y2 > threshold:
i have a problem and i hope that i will find help there.
This is my example code. Its only part of my algorithm. For imagine, how the point are moving, during the equations, i need show contour of function with two variables and into the points. Becase i have more difficult function than parabolic function, so the equations are too long than i need. For this reason i move contour ploting before the loop. But i have problem. I need show countour always and points only for i-loop and my solution doesnt work. Please help me!
[R S] = meshgrid(-5:0.1:5, -5:0.1:5);
figure
contour(R, S, R.^2 + S.^2, 5);
axis([-5,5,-5,5])
axis square
hold on
for i=1:50
a = 0;
b = 1:2
B = repmat(b,5,1)
A = unifrnd(a,B)
x = A(1:5,1);
y = A(1:5,2);
scatter(x,y,'fill')
hold off
pause(0.5)
end
You should store the handle to your scatter plot and simply update the XData and YData properties of it rather than destroying the plot objects every time
[R S] = meshgrid(-5:0.1:5, -5:0.1:5);
figure
contour(R, S, R.^2 + S.^2, 5);
axis([-5,5,-5,5])
axis square
hold on
% Create a scatter plot and store the graphics handle so we can update later
hscatter = scatter(NaN, NaN, 'fill');
for i=1:50
a = 0;
b = 1:2
B = repmat(b,5,1)
A = unifrnd(a,B)
x = A(1:5,1);
y = A(1:5,2);
% Update the X and Y positions of the scatter plot
set(hscatter, 'XData', x, 'YData', y);
pause(0.5)
end
I need to program a dot moving towards another dot. I have the initial coords of the point, however the point at which it has to move to is randomly selected.Eg the initial of the dot might me [0 0], and it will have to travel to [100,325] or [198,-243]. The grid within which the points can spawn has bound of 500 and -500 (square).
Currently generating points using,
dots = plot(XY(:,1), XY(:,2), ...
'Marker', '.', ...
'Color', 'b', ...
'LineStyle', 'none', ...
'MarkerSize', 6);
and giving them XY coordinates with (initially random within a small starting area). In this case numberDots is = 1 (have to add more later). baseRadius = 50
angle = rand(numberUAVs, 1) * 2*pi;
r = baseRadius * sqrt(rand(numberDots, 1));
XY = [r .* cos(angle(:)) + 0, ...
r .* sin(angle(:)) + 0]
This is how im currently trying to get them to move by simply adding 1 to the coords and plotting.
for i = 1:1000000
XY = XY + 1;
pause(0.1)
set(dots, 'XData', XY(:,1), 'YData', XY(:,2));
end
How can make the dot move towards a randomly defined point. Thanks
Adding 1 to you x/y coordinates each time through the loop will work only if your new point is always to the upper right of your current point with the same x and y distance from your start point.
Instead, you can simply use linspace to get linearly spaced x values between the start and finish and similarly for y values.
%// Number of "steps" you want to take to get from the start point to the end point
nSteps = 100;
%// Figure out all intermediate x/y values between the two
xx = linspace(x_start, x_end, nSteps);
yy = linspace(y_start, y_end, nSteps);
%// Create the initial plot
plt = plot(NaN, NaN, 'bo');
%// Then plot the point's path
for k = 1:nSteps
set(plt, 'XData', xx(k), 'YData', yy(k))
drawnow;
end
Let me start by saying that I suspect this is a very simple solution that I am somehow barely missing.
I'm trying to write a script that will plot one set of data in four separate subplots (each showing a different view of a 3D shape the points are being plot in), but I only want to show the current points - as in, I don't want to see every point, just one set of points as they progresses with time (I'm capturing video of the plot to visualize movement with time). However, for every instant in time, there are n points to plot simultaneously. I know this should be simple, but I can't manage to get all n points to plot at once - I can only seem to get it to plot one point at a time, which is pretty meaningless when you have n markers moving with time, all of which you'd like to see moving at the same time.
The following code works to plot every point in sequence, but does not plot all n points together, refreshing those points for every t:
n = 0;
for i = 1:length(data)
% every marker occurs in one row of a specific set of data, and is split
% into x, y, z, so I correct here for each marker being every 3rd column
for j = 1:(cind/3) % cycle through every marker
x = markerLoc(i, j*3 - 2);
y = markerLoc(i, j*3 - 1);
z = markerLoc(i, j*3);
if j == 1 && i == 1 % set up the initial plots for each subplot
% s1,2,3,4 are the handles for the subplots
h1 = scatter3(s1,x, y, z, 'MarkerFaceColor', [0 .75 .75],...
'MarkerEdgeColor','k');
h2 = scatter3(s2,x, y, z, 'MarkerFaceColor', [0 .75 .75],...
'MarkerEdgeColor','k');
h3 = scatter3(s3,x, y, z, 'MarkerFaceColor', [0 .75 .75],...
'MarkerEdgeColor','k');
h4 = scatter3(s4,x, y, z, 'MarkerFaceColor', [0 .75 .75],...
'MarkerEdgeColor','k');
else % update data
% this is probably insanely redundant
set(h1, 'XData', x, 'YData', y, 'ZData', z);
set(h2, 'XData', x, 'YData', y, 'ZData', z);
set(h3, 'XData', x, 'YData', y, 'ZData', z);
set(h4, 'XData', x, 'YData', y, 'ZData', z);
end
end
frames(n) = getframe(gcf); % capture frames
n = n + 1;
end
Can anyone help find what I need to change here to make it plot, instead of after every j (individual marker), after ever nth j?
As it is, you are currently only updating the XData, YData, and ZData for one marker at each instant at a time. Instead you want to get rid of the inner loop and get an array of x, y, and z variables. You can then use these for the scatter3 calls as well as to update the XData, YData, and ZData.
for i = 1:length(data)
%// Get XYZ coordinates for all markers at this time and reshape so X,Y,Z are rows
xyz = reshape(markerLoc(i,:), 3, []);
if i == 1
%// Put these in an array so we can update them easier
h(1) = scatter3(s1, xyz(1,:), xyz(2,:), xyz(3,:), ...
'MarkerFaceColor', [0 .75 .75],...
'MarkerEdgeColor','k');
%// Just use copyobj to make a copy of this plot to all axes
h(2:4) = copyobj(h(1), [s2, s3, s4]);
else
set(h, 'XData', xyz(1,:), 'YData', xyz(2,:), 'ZData', xyz(3,:))
end
end
I have a matrix with n rows and 4 columns. The columns are x0, y0, and x1, y1 (so basically I have n pairs of point coordinates in 2D). I want to draw a line between corresponding point pairs (that is, only between x0, y0 and x1, y1 of one row).
Is it possible to do it without a loop? Because the following works but is very slow.
for i = 1:size(A.data, 1)
plot([A.data(i, 1), A.data(i, 3)], [A.data(i, 2), A.data(i, 4)], 'k-')
end
I came here looking for the same answer. I basically want a horizontal line for each x,y point, starting with that point's x-y value, and ending at the x value of the next xy pair, without a line joining that segment to that of the next xy pair. I can make the segments by adding new points between, with the old y and the new x, but I didn't know how to break up the line segments. But your wording (matrix) gave me an idea. What if you load your xy pairs to a pair of x, y vectors and - wait for it - separate your pairs with nan's in both the x and y vectors. I tried that with a very long sine wave, and it seems to work. A ton of disjoint line segments, that plot and zoom instantly. :) See if it solves your problem.
% LinePairsTest.m
% Test fast plot and zoom of a bunch of lines between disjoint pairs of points
% Solution: put pairs of x1,y1:x2,y2 into one x and one y vector, but with
% pairs separated by x and or y = nan. Nan is wonderful, because it leaves
% your vector intact, but it doesn't plot.
close all; clear all;
n = 10000; % lotsa points
n = floor(n/3); % make an even set of pairs
n = n * 3 - 1; % ends with a pair
x = 1:n; % we'll make a sine wave, interrupted to pairs of points.
% For other use, bring your pairs in to a pair of empty x and y vectors,
% padding between pairs with nan in x and y.
y = sin(x/3);
ix = find(0 == mod(x,3)); % index 3, 6, 9, etc. will get...
x(ix) = nan; % nan.
y(ix) = nan; % nan.
figure;
plot(x,y,'b'); % quick to plot, quick to zoom.
grid on;
This works for the data structure I have:
data = [
0, 0, 1, 0;...
1, 0, 1, 1;...
1, 1, 0, 1;...
0, 1, 0, 0 ...
];
figure(1);
hold off;
%slow way
for i = 1:size(data, 1)
plot([data(i, 1) data(i, 3)], [data(i, 2) data(i, 4)], 'r-');
hold on;
end
%fast way ("vectorized")
plot([data(:, 1)' data(:, 3)'], [data(:, 2)' data(:, 4)'], 'b-');
axis equal
This particular example draws a square.
The key is that MATLAB draws lines column-wise in the arguments. That is, if the arguments of plot have n columns, the line will have n-1 segments.
In a "connect-the-dots" scenario where all points in the vectors must be connected, this is irrelevant because MATLAB will transpose to get a column vector if it needs to. It becomes important in my application because I do not want to connect every point on the list - only pairs of points.
Try line for example
X=[1:10 ; 2*(1:10)];
Y=fliplr(X);
line(X,Y)