Matlab generate smooth curve between scatter points - matlab

I need to generate a curve between scatter points then identify the unit normal of the curve at each point. Here is an example of a point cloud
figure
x = [1 2 0 0 1 1 2 3 4 2];
y = [4 6 9 1 1 2 4 9 2 3];
scatter(x,y)
hold on
line(x,y)
xlim([0 4])
ylim([0 10])
NOTE: the 2 points along the y-axis are connected
Instead of a line between the points, I'd like to create a smooth curve. I'm not sure how to do this when points in x and y repeat. An attempt using spline fails. After I know the curve, I need to find the unit normals at each point. How do I go about this?
EDIT:
Basically I want to do what is show here for polyfit in the matlab docs. Assuming that x was unique in my case, this wouldn't be an issue. I could identify the polynomial and then, I believe, determine the unit normals from the polynomial function evaluated at that point. But in my case, the x and y data repeat so a straight forward application doesn't work.

One way to get a smooth path is to treat this as a parametric function and interpolate x and y separately.
x = [1 2 0 0 1 1 2 3 4 2];
y = [4 6 9 1 1 2 4 9 2 3];
t = 1:numel(x);
tq = 1:0.1:t(end);
xq = interp1(t,x,tq,'v5cubic');
yq = interp1(t,y,tq,'v5cubic');
plot(x,y,' ob',xq,yq,'-r');
To estimate the normals you can take the average normal of the two line segments around the sample points. This code is a bit ugly but it gets the job done.
n = zeros(2,numel(x));
for tidx = 1:numel(t)
tt = t(tidx);
idx1 = find(tq <= tt,1,'last');
idx0 = idx1 - 1;
idx2 = idx1 + 1;
if idx0 > 0
n1 = [yq(idx1) - yq(idx0); xq(idx0) - xq(idx1)];
n(:,tidx) = n(:,tidx) + n1/norm(n1);
end
if idx2 <= numel(tq)
n2 = [yq(idx2) - yq(idx1); xq(idx1) - xq(idx2)];
n(:,tidx) = n(:,tidx) + n2/norm(n2);
end
n(:,tidx) = n(:,tidx) / norm(n(:,tidx));
end
plot(x,y,' ob',xq,yq,'-r',[x.' x.'+n(1,:).'].', [y.' y.'+n(2,:).'].',' -k');
axis equal;
If you use pchip instead of v5cubic for the interpolation method then you get more symmetry around the sample points. However, it appears that any sharp turns (90 degrees or greater) are not smoothed.

Related

How to plot mesh (with intensity profile on z) Nx3 matrix in Octave/Matlab?

I have got a data that are in a matrix of size N rows by 3 columns, each column corresponds to particular point in x, y and z axis. The data in that matrix have already been pre generated so my task is to plot it in a mesh as this is faster than creating the same plot with scatter function requiring 1M data points just to look similar.
The z will determine the corresponding color intensity as well as the valley and hills of the mesh.
Consider the example below:
A = [1 2 3; 1 3 2; 1 5 8; 1 2 6; 6 1 2];
mesh(A(:,1),A(:,2),A(:,3));
The attempt was nice, as I have already supplied appropriate x, y, and z for mesh function. However, I just get empty plot when I tested it. So, I am looking for help on how to plot mesh (with intensity profile on z) Nx3 matrix in Octave/Matlab?
Read about delaunay triangulation. YOu can make an unstructured mesh from your (x,y,z) data and then plot the mesh.
A = [1 2 3; 1 3 2; 1 5 8; 1 2 6; 6 1 2];
% mesh(A(:,1),A(:,2),A(:,3));
x = A(:,1) ;
y = A(:,2) ;
z = A(:,3) ;
dt = delaunayTriangulation(x,y) ;
triplot(dt) ;

Plotting equation in Matlab using for loop

I want to plot an equation using a for-loop. I have tried several different ways, but keep getting the apparently common error "Subscript indices must either be real positive integers or logicals". The equation I want to plot is y(x) = (x^4)-(4*x^3)-(6*x^2)+15.
The last code I tried was the following:
y(0) = 15;
for x = [-3 -2 -1 0 1 2 3 4 5 6];
y(x) = (x^4)-(4*x^3)-(6*x^2)+15;
end
plot(x,y)
To start from the beginning,
y(0) = 15;
will give you the following error:
Subscript indices must either be real positive integers or logicals.
This is because Matlab's indexing starts at 1. Other languages like C and Python start at 0.
Matlab can work directly with vectors. So in your code, there is no need for a loop at all.
You can do it like this:
x = [-3 -2 -1 0 1 2 3 4 5 6];
y = (x.^4) - (4*x.^3) - (6*x.^2) + 15;
plot(x, y);
Note that we need to use element-wise operators like .* and .^ to calculate the values vectorized for every element. Therefore a point . is written in front of the operator.
Additionally, we can improve the code substantially by using the colon operator to generate x:
x = -3:6; % produces the same as [-3 -2 -1 0 1 2 3 4 5 6]
y = (x.^4) - (4*x.^3) - (6*x.^2) + 15;
plot(x, y);
If you want finer details for your graph, use linspace as suggested by #Yvon:
x = linspace(-3, 6, 100); % produces a vector with 100 points between -3 and 6.
y = x.^4-4*x.^3-6*x.^2+15;
plot(x,y)
x = linspace(-3, 6, 100);
y = x.^4-4*x.^3-6*x.^2+15;
plot(x,y)

Clampled cubic splines with flat extrapolation

I am trying to implement a clamped cubic spline with a zero slope (flat extrapolation) at the boundary knots, but I am unable to obtain the desired results.
For instance setting:
x = [3 4 7 9];
y = [2 1 2 0.5];
I can use the CSAPE function to obtain the piecewise polynomial
pp = csape(x,y,'variational');
Next, evaluating the pp in the range [0-10] yields,
xx = 0:0.1:10;
yy =ppval(pp,xx);
plot(xx,yy)
However, this method do not achieve a flat extrapolation outside the [3-9] range (i.e for x<3 all values for y should be 2, and for x>9 all values for y should be 0.5)
Is there any way to achieve the desired result?
Edit: Continuity at the boundary knot should be preserved
I don't think there's any need to use csape, you can just use spline. From the documentation for spline:
If Y is a vector that contains two more values than x has entries, the
first and last value in Y are used as the endslopes for the cubic
spline.
Also, spline allows you to obtain the interpolated yy values directly, so:
x = [3 4 7 9];
y = [2 1 2 0.5];
xx = 0:0.1:10;
yy = spline(x,[0 y 0], xx);
plot(xx,yy)
This gives me the plot below.
Looking at this, the slope is zero at the boundaries (x=3 and x=9), which is what we are asking of a 'clamped' spline with zero gradient at the boundaries. If you wish to have zero gradient beyond the boundaries, I would recommend just doing the following:
yy(xx<x(1)) = y(1);
yy(xx>x(length(x))) = y(length(y));
Giving:
Edit
This gives a continuous y function at the end-knots, but y' is not smooth at the end-knots. If you would like y' to be smooth you could pad your input arrays and use this as the input to your spline function. This would give you some oscillations as shown below, though, which may or may not be what you want.
% Spline on padded input arrays
x = [0 1 2 3 4 7 9 10 11 12];
y = [2 2 2 2 1 2 0.5 0.5 0.5 0.5];
yy = spline(x,y, xx);

MatLab--How would I generate an n-sided shape wher n >= 4

I'm new to Matlab but I know a bit about programming.
For class, we have been asked to generate a matrix that gives the vertices of a two dimensional n-sided shape where n>=4. Then, generate the vectors to connect the vertices. We were also given a hint: a vector for each segment can be found by adding the vectors drawn from the origin to each of two adjacent vertices.
I know how to create a matrix using A = [1 1; 1 2; 2 2; 2 1] but I'm not sure how to draw the vectors given this or any other matrix.
The plot() function looks promising, but I'm unsure how to use it with the matrix.
Thank you for any suggestions.
Btw, I'm using Matlab 2011a
I'm not exactly sure how your matrix represents your shape but you might for example let the x-coordinates of the shape be the first column of your array, then let the y-coordinates be the 2nd column, like:
A = [1 1; 1 2; 2 2; 2 1];
x = A(:,1);
y = A(:,2);
fill(x,y,'g');
axis([0 3 0 3]);
axis square;
Which in your case plots a square from the matrix A:
Or construct something a little more complicated like a pentagon:
theta = [0:pi/2.5:2*pi];
x = sin(theta);
y = cos(theta);
% your matrix is then:
B(:,1) = x;
B(:,2) = y;
B
figure;fill(x,y,'g');
axis square;
Which gives:
If you just want to plot the outline with plot (not fill the interior with fill), just remember you have to repeat the initial point at the end so that the polygonal line is closed:
A = [1 1; 1 2; 2 2; 2 1];
B = [A; A(1,:) ]; %// repeat first row at the end
plot(B(:,1),B(:,2))
axis equal %// same scale on both axes
axis([min(x)-.5 max(x)+.5 min(y)-.5 max(y)+.5]) %// larger axes for better display

Get list of Y's on linspace from X's and Y's arrays in Matlab

I have two arrays - X points and Y points. X array have some spaces (e. g. [0 1 2 6 7 8]), and Y array contains only values for that Xes. I've got that array as a local maxima from wavelet transform. I can plot it with plot(X,Y)
Now I want to get Y's on linspace - Y must contain values for any X from 0 to 8. I want to have the same plot plot(Y) as the previous plot(X, Y).
How can I do this?
It looks like you want to perform interpolation
xPts = [0 1 2 6 7 8];
yPts = ...
xPlot = 0:1:8;
yPlot = interp1(xPts,yPts,xPlot,'cubic')
plot(xPlot,yPlot)
Check the documentation for interp1 for the different interpolation schemes.
If there are repeated x-values, you can average the corresponding y-values
xPtsRep = [0 0 1 2 6 7 7 8]
yPtsRep = ...
[xPts,~,xIdx] = unique(xPtsRep);
yPts = accumarray(xIdx,yPtsRep,[],#mean);