creating exponentially decaying beams on a matlab plot - matlab

I am having some trouble figuring out how to create these beams in my plot in matlab.
I have created this graphic that I need to use before the beams are added, but I am not sure how to proceed.
The beams should decay (or attenuate) exponentially from the surface with a decay constant of 15 cm. So, if a surface tile has energy energy value 1, at depth i, where i represents the depth in centimeters, the value should be exp(-i/15). Note that the beams should cover the entire extent of the middle square.
the graphic:
My code for this graphic:
Rows = 30;
Cols = 40;
body = zeros(Rows, Cols);
clims = [-1 1];
imagesc(body, clims);
hold on
x = 0:5:40;
y = 0:5:30;
imagesc(x, y, body)
axis([0 40 0 30])
axis xy
shading flat
colorbar
hold on
plot([18, 22],[13, 13],'k')
hold on
plot([18, 18],[13, 17],'k')
hold on
plot([18, 22],[17, 17],'k')
hold on
plot([22, 22],[13, 17],'k')
Any help would be so much appreciated!

There's a few ways to do this. Basically, you want to set the values in your body before you plot them. Here's one example of a similar curve, I'll let you figure out how to extrapolate it to your exact needs.
Rows = 30;
Cols = 40;
body = zeros(Rows, Cols);
xsize=size(body,1);
ysize=size(body,2);
for x=1:xsize
for y=1:ysize
body(x,y)=exp((y-ysize/2)*(x-xsize/2));
end
end
x = 0:5:40;
y = 0:5:30;
imagesc(x, y, body)

Related

Matlab plot: equal distance between ticks

I have this 3D plot that I'm making but the data are not linear. This implies that on my plot, the distance between the ticks that I want to show is not equal. How can I adapt the scale of the x and y axis so that this is the case, i.e. so that the axis gets divided into equal parts with the current ticks?
I want the same ticks and tick labels, but that they just have an equal distance in between them on the axes, in stead of small between 0.1 and 0.5 and large between 1 and 5.
The current plot looks like this:
RMSEval = xlsread('RMSEvalues.xlsx');
X = RMSEval(:,1);
Y = RMSEval(:,2);
Z = RMSEval(:,3);
figure(1);
xi = linspace(min(X),max(X),30);
yi= linspace(min(Y),max(Y),30);
[XI,YI] = meshgrid(xi,yi);
ZI = griddata(X,Y,Z,XI,YI);
contourf(XI,YI,ZI);
colormap('jet');
xticks([1e-13 5e-13 1e-12 5e-12 1e-11]);
yticks([1e-18 5e-18 1e-17 5e-17 1e-16]);
colorbar;
A plot where the distance between 0.1 and 0.5 is the same as between 1 and 5 would be a log plot, specifically a log-log plot since you want it on both axes. One way to achieve this would be to transform the X and Y values of your data logarithmically and modify the tick mark labels to match the untransformed values rather than the logarithmic ones you're actually plotting.
A rough guess at a solution is below. I say a rough guess because without posting the data you're importing from the xlsx file or a paired down version of that data (as in an MWE) I can't actually test it.
RMSEval = xlsread('RMSEvalues.xlsx');
X = log(RMSEval(:,1));
Y = log(RMSEval(:,2));
Z = RMSEval(:,3);
figure(1);
xi = linspace(min(X),max(X),30);
yi= linspace(min(Y),max(Y),30);
[XI,YI] = meshgrid(xi,yi);
ZI = griddata(X,Y,Z,XI,YI);
contourf(XI,YI,ZI);
colormap('jet');
xticks(log([1e-13 5e-13 1e-12 5e-12 1e-11]));
xticklabels(cellfun(#num2str,num2cell(),'UniformOutput',false));
yticks(log([1e-18 5e-18 1e-17 5e-17 1e-16]));
yticklabels(cellfun(#num2str,num2cell([1e-18 5e-18 1e-17 5e-17 1e-16]),'UniformOutput',false));
colorbar;

Translating 2D image with RGB colours along axes

I am trying to create my own voronoi diagram. I have an arbitrary shape defined by their x and y coordinates stored in separate vectors. Inside this shape are some points of interest (with known coordinates) that belong to two different groups and act as seeds to the voronoi diagram. As an example, the whole diagram ranges from x=-10 to x=90 and y=-20 to y=60. The boundary shape is not rectangular but falls within the axes range above.
What I have done so far is to create a 3D matrix (100 X 80 X 3), C, with all ones so that the default colour will be white. I then looped through from i=1:100, j=1:80, testing individual pixels to see if they fall within the shape using inpolygon. If they do, I then find out which point is the pixel closest to and assign it a colour based on whether the closest point belongs to group 1 or 2.
All is good so far. I then used imagesc to display the image with a custom axis range. The problem is that the voronoi diagram has the general shape but it is off in terms of position as the pixel coordinates are different from the actual world coordinates.
I tried to map it using imref2d but I do not know how it really works or how to display the image after using imref2d. Please help me on this.
I am open to other methods too!
Thank you!
Edit:
As requested, let me give a more detailed example and explanation of my problem.
Let us assume a simple diamond shape boundary with the following vectors and 4 points with the following coordinate vectors:
%Boundary vectors
Boundary_X = [-5 40 85 40 -5];
Boundary_Y = [20 50 20 -10 20];
%Point vectors
Group_One_X = [20 30];
Group_One_Y = [10 40];
Group_Two_X = [50 70];
Group_Two_Y = [5 20];
Next I plot all of them, with different groups having different colours.
%Plot boundary and points
hold on
plot(Boundary_X,Boundary_Y)
scatter(Group_One_X,Group_One_Y,10,'MarkerFaceColor','Black',...
'MarkerEdgeColor','Black')
scatter(Group_Two_X,Group_Two_Y,10,'MarkerFaceColor','Red',...
'MarkerEdgeColor','Red')
hold off
axis([-10, 90, -20, 60])
This is the result:
Boundary with points
Next I test the whole graph area pixel by pixel, and colour them either cyan or yellow depending on whether they are closer to group 1 or 2 points.
%Create pixel vector with default white colour
C=ones(100,80,3);
Colour_One = [0 1 1];
Colour_Two = [1 1 0];
%Loop through whole diagram
for i=1:100
for j=1:80
x=i;
y=j
if inpolygon(x,y,Boundary_X,Boundary_Y)
%Code for testing which point is pixel closest to
%If closest to group 1, assign group 1 colour, else group 2
%colour
end
end
end
%Display image
hold on
imagesc(C)
hold off
This is the result
Failed Voronoi Diagram
The shape is somewhat correct for the right side but not for the others. I understand that this because my world coordinates start from negative values but the pixel coordinates start from 1.
Hence I am at a lost as to how can I solve this problem.
Thank you!
One thing to notice is that you have to convert between image and plot coordinates. The plot coordinates are (x,y) where x goes to the right, and y goes up. The matrix coordinates are (i,j) where i goes down, and j to the right. An easy way to do this is with the use of vec_X,vec_Y as shown below.
Another solution for the newer Matlab versions would be - as you said - using imref2d but unfortunately I have no experience with that command.
%Boundary vectors
Boundary_X = [-5 40 85 40 -5];
Boundary_Y = [20 50 20 -10 20];
%Point vectors
Group_One_X = [20 30];
Group_One_Y = [10 40];
Group_Two_X = [50 70];
Group_Two_Y = [5 20];
%Coordinate system
min_X = -10;
max_X = 90;
min_Y = -20;
max_Y = 60;
axis([min_X, max_X, min_Y, max_Y])
%Create pixel vector with default white colour
rows_N = 100;
columns_N = 80;
C=ones(rows_N,columns_N,3);
%These vectors say where each of the pixels is in the plot coordinate
%system
vec_X = linspace(min_X,max_X,columns_N);
vec_Y = linspace(min_Y,max_Y,rows_N);
Colour_One = [0 1 1];
Colour_Two = [1 1 0];
%Loop through whole diagram
for i=1:100
for j=1:80
if inpolygon(vec_X(j),vec_Y(i),Boundary_X,Boundary_Y)
%calculate distance to each point
Distances_One = zeros(size(Group_One_X));
Distances_Two = zeros(size(Group_Two_X));
for k=1:numel(Group_One_X);
Distances_One(k) = norm([Group_One_X(k),Group_One_Y(k)]-[vec_X(j),vec_Y(i)]);%assuming euclidean norm, but can be adjusted to whatever norm you need
end
for k=1:numel(Group_Two_X);
Distances_Two(k) = norm([Group_Two_X(k),Group_Two_Y(k)]-[vec_X(j),vec_Y(i)]);%assuming euclidean norm, but can be adjusted to whatever norm you need
end
if min(Distances_One) < min(Distances_Two);
C(i,j,:) = Colour_One;
else
C(i,j,:) = Colour_Two;
end
end
end
end
%Display image
imagesc(vec_X,vec_Y,C) %lets you draw the image according to vec_X and vec_Y
%Plot boundary and points
hold on
plot(Boundary_X,Boundary_Y)
scatter(Group_One_X,Group_One_Y,10,'MarkerFaceColor','Black',...
'MarkerEdgeColor','Black')
scatter(Group_Two_X,Group_Two_Y,10,'MarkerFaceColor','Red',...
'MarkerEdgeColor','Red')
hold off

3d plot with axes of same length

I have some 3D trajectories I want to plot.
Since they vary a lot in XY, but much less in Z, the default plot3 is misleading, because it automatically scales axes.
I've been told to use axes equal but it has no effect (see the commented line where I used it).
I came up with this code, which in my opinion is very long to achieve a so simple task:
[D,rate]=read_vicon_ascii('csvdata/a1-0.csv');
% or replace above line with D=csvread('stackoverflow-31289872.csv');
% get stackoverflow-31289872.csv at https://drive.google.com/file/d/0B5GjKiDZk3F5UHlVQUxKeFo4SG8/view?pli=1
% indices of X,Y,Z columns
X = 1+(2:3:14);
Y = 2+(2:3:14);
Z = 3+(2:3:14);
Bounds = [ min(min(D(:,X))) max(max(D(:,X)))
min(min(D(:,Y))) max(max(D(:,Y)))
min(min(D(:,Z))) max(max(D(:,Z))) ];
MaxDelta = max(Bounds(:,2)-Bounds(:,1));
SquareBounds = Bounds;
for xyz=1:3
Delta = SquareBounds(xyz,2) - SquareBounds(xyz,1);
SquareBounds(xyz,:) = SquareBounds(xyz,:) + (MaxDelta - Delta) * [-0.5 0.5];
end
figure
hold on
for i=1:5
plot3(D(:,X(i)),D(:,Y(i)),D(:,Z(i)),'r-')
end
xlim(SquareBounds(1,:))
ylim(SquareBounds(2,:))
zlim(SquareBounds(3,:))
%axes equal
hold off
Is there any way to make it better. (or a correct usage of axes equal if that does what is supoosed to do?)

Matlab Plotting roots (zeros and poles) of Z-Transform

I need to plot the roots onto a transfer function H(z) overlaying a unit circle, giving enough room to see all points. I'm able to get the roots from H(z) when it is given in the form zeros = [z0 z1 z2...], poles = [p0 p1 p2]. Using Matlab's roots function, I'm able to get the pole and zero locations. My Matlab code so far is
function zplot(b, a)
b_roots = roots(b);
a_roots = roots(a);
hold on
rectangle('Position',[-1 -1 2 2],'Curvature',[1 1]);
plot(b_roots,'x blue');
plot(a_roots,'o blue');
axis %need axis to be equal and +10percent of maximum value
hold off
end
So far, I can plot the roots and the unit circle, but I need help with adjusting the axes so that they are 1) equal to each other and 2) 10% more than the highest value. I'm not sure how to go about doing this part. I tried making a variable lim_max = max(b_roots,a_roots) but it ended up being an array, and wouldn't work in the axis([-lim_max lim_max -lim_max lim_max]) function. I need the plot to scale to +10% with the inputs as they change.
Side note (not necessary): is there a way for it to look like a circle when I plot it, because right now it ends up looking like an oval most of the time. I can readjust the screen, which is fine, but if there's an easy way to do that, I'd also like to know.
Set axis equal and calculate min/max:
function zplot(b, a)
b_roots = roots(b);
a_roots = roots(a);
xlimits = [min(min([real(a_roots);real(b_roots)])), max(max([real(a_roots);real(b_roots)]))];
ylimits = [min(min([imag(a_roots);imag(b_roots)])), max(max([imag(a_roots);imag(b_roots)]))];
hold on
rectangle('Position',[-1 -1 2 2],'Curvature',[1 1]);
plot(b_roots,'x black');
plot(a_roots,'o blue');
axis equal;
xlim(1.1*xlimits);
ylim(1.1*ylimits);
hold off
end
Use the following code. This will 1) find the maximum overall limits of x and y axes 2) set those limits equal to each other 3) plot those limits +10%
b_roots = roots(b);
a_roots = roots(a);
x_min = min(min([real(a_roots);real(b_roots)]));
x_max = max(max([real(a_roots);real(b_roots)]));
y_min = min(min([imag(a_roots);imag(b_roots)]));
y_max = max(max([imag(a_roots);imag(b_roots)]));
%get the magnitude of the overall minimum value
min_lim = abs(min(x_min,y_min));
%abs may not be necessary
max_lim = abs(max(x_max,y_max));
%set high and low limits equal to each other from negative to positive
eq_limit = [-max(min_lim,max_lim),max(min_lim,max_lim)];
hold on
rectangle('Position',[-1 -1 2 2],'Curvature',[1 1]);
plot(b_roots,'x black');
plot(a_roots,'o blue');
axis equal;
xlim(1.1*eq_limit);
ylim(1.1*eq_limit);
hold off
Thanks to #M.S. for their answer and help.

How to fit a curve by a series of segmented lines in Matlab?

I have a simple loglog curve as above. Is there some function in Matlab which can fit this curve by segmented lines and show the starting and end points of these line segments ? I have checked the curve fitting toolbox in matlab. They seems to do curve fitting by either one line or some functions. I do not want to curve fitting by one line only.
If there is no direct function, any alternative to achieve the same goal is fine with me. My goal is to fit the curve by segmented lines and get locations of the end points of these segments .
First of all, your problem is not called curve fitting. Curve fitting is when you have data, and you find the best function that describes it, in some sense. You, on the other hand, want to create a piecewise linear approximation of your function.
I suggest the following strategy:
Split manually into sections. The section size should depend on the derivative, large derivative -> small section
Sample the function at the nodes between the sections
Find a linear interpolation that passes through the points mentioned above.
Here is an example of a code that does that. You can see that the red line (interpolation) is very close to the original function, despite the small amount of sections. This happens due to the adaptive section size.
function fitLogLog()
x = 2:1000;
y = log(log(x));
%# Find section sizes, by using an inverse of the approximation of the derivative
numOfSections = 20;
indexes = round(linspace(1,numel(y),numOfSections));
derivativeApprox = diff(y(indexes));
inverseDerivative = 1./derivativeApprox;
weightOfSection = inverseDerivative/sum(inverseDerivative);
totalRange = max(x(:))-min(x(:));
sectionSize = weightOfSection.* totalRange;
%# The relevant nodes
xNodes = x(1) + [ 0 cumsum(sectionSize)];
yNodes = log(log(xNodes));
figure;plot(x,y);
hold on;
plot (xNodes,yNodes,'r');
scatter (xNodes,yNodes,'r');
legend('log(log(x))','adaptive linear interpolation');
end
Andrey's adaptive solution provides a more accurate overall fit. If what you want is segments of a fixed length, however, then here is something that should work, using a method that also returns a complete set of all the fitted values. Could be vectorized if speed is needed.
Nsamp = 1000; %number of data samples on x-axis
x = [1:Nsamp]; %this is your x-axis
Nlines = 5; %number of lines to fit
fx = exp(-10*x/Nsamp); %generate something like your current data, f(x)
gx = NaN(size(fx)); %this will hold your fitted lines, g(x)
joins = round(linspace(1, Nsamp, Nlines+1)); %define equally spaced breaks along the x-axis
dx = diff(x(joins)); %x-change
df = diff(fx(joins)); %f(x)-change
m = df./dx; %gradient for each section
for i = 1:Nlines
x1 = joins(i); %start point
x2 = joins(i+1); %end point
gx(x1:x2) = fx(x1) + m(i)*(0:dx(i)); %compute line segment
end
subplot(2,1,1)
h(1,:) = plot(x, fx, 'b', x, gx, 'k', joins, gx(joins), 'ro');
title('Normal Plot')
subplot(2,1,2)
h(2,:) = loglog(x, fx, 'b', x, gx, 'k', joins, gx(joins), 'ro');
title('Log Log Plot')
for ip = 1:2
subplot(2,1,ip)
set(h(ip,:), 'LineWidth', 2)
legend('Data', 'Piecewise Linear', 'Location', 'NorthEastOutside')
legend boxoff
end
This is not an exact answer to this question, but since I arrived here based on a search, I'd like to answer the related question of how to create (not fit) a piecewise linear function that is intended to represent the mean (or median, or some other other function) of interval data in a scatter plot.
First, a related but more sophisticated alternative using regression, which apparently has some MATLAB code listed on the wikipedia page, is Multivariate adaptive regression splines.
The solution here is to just calculate the mean on overlapping intervals to get points
function [x, y] = intervalAggregate(Xdata, Ydata, aggFun, intStep, intOverlap)
% intOverlap in [0, 1); 0 for no overlap of intervals, etc.
% intStep this is the size of the interval being aggregated.
minX = min(Xdata);
maxX = max(Xdata);
minY = min(Ydata);
maxY = max(Ydata);
intInc = intOverlap*intStep; %How far we advance each iteraction.
if intOverlap <= 0
intInc = intStep;
end
nInt = ceil((maxX-minX)/intInc); %Number of aggregations
parfor i = 1:nInt
xStart = minX + (i-1)*intInc;
xEnd = xStart + intStep;
intervalIndices = find((Xdata >= xStart) & (Xdata <= xEnd));
x(i) = aggFun(Xdata(intervalIndices));
y(i) = aggFun(Ydata(intervalIndices));
end
For instance, to calculate the mean over some paired X and Y data I had handy with intervals of length 0.1 having roughly 1/3 overlap with each other (see scatter image):
[x,y] = intervalAggregate(Xdat, Ydat, #mean, 0.1, 0.333)
x =
Columns 1 through 8
0.0552 0.0868 0.1170 0.1475 0.1844 0.2173 0.2498 0.2834
Columns 9 through 15
0.3182 0.3561 0.3875 0.4178 0.4494 0.4671 0.4822
y =
Columns 1 through 8
0.9992 0.9983 0.9971 0.9955 0.9927 0.9905 0.9876 0.9846
Columns 9 through 15
0.9803 0.9750 0.9707 0.9653 0.9598 0.9560 0.9537
We see that as x increases, y tends to decrease slightly. From there, it is easy enough to draw line segments and/or perform some other kind of smoothing.
(Note that I did not attempt to vectorize this solution; a much faster version could be assumed if Xdata is sorted.)