Use Matlab trisurf to plot element-wise quantity - matlab

Usually, trisurf is used to plot nodal quantity on a triangular mesh. However, I would like to plot some element-wise quantity using the following command.
trisurf(t,p(:,1),p(:,2),elewise_quantity,'edgecolor','k','facecolor','interp');
Where elewise_quantiy should be of the same dimension as p(:,1) or p(:,2), so I create elewise_quantity by associating the element-wise quantity to each node of that element.
In this specific case, the 8 triangular element in the middle are associated with 1 and all other elements are 0-valued. There are 10*10 little squares and 10*10*2 little triangulars.
The problem is, as shown in the picture, trisurf can't produce the effect I want. What I expect is an "exact element-wise description", i.e sharp transition at the edge
Also notice that at each corner, the display is different, this's due to the specific orientation of the triangular, is there an elegant way to deal with it?

If you need sharp discontinuities across elements, I'd recommend not displaying nodal values. Calculate element values at internal integration points (e.g. element average at centroid) and assign that value to the element. Ask Matlab to color each element according to its centroidal or integration point value.

You can set per-triangle colors using this method:
%// Example data
X = rand(100,1);
Y = rand(100,1);
Z = X+Y;
T = delaunay(X,Y);
C = mean(Z(T),2);
%// Plot the data
hh = trisurf(T,X,Y,Z);
set(gca, 'CLim', [min(C), max(C)]);
set(hh,'FaceColor', 'flat', ...
'FaceVertexCData', C, ...
'CDataMapping', 'scaled');
If you also don't want the see the triangles that appear at the boundary, you won't get this behavior from a single call to trimesh. You would need to compute a per-triangle quantity and then drop those you don't want to plot according to the per-triangle quantity.

Related

Matlab Surface Plot

I have a set of data points, x, y, and z contained in a matrix, record.
In record, each row is a data-point where the first value is the x-coordinate, the second is the y-coordinate, and the third is the z-coordinate. I would like to represent this as a surface plot. I tried:
surf([record(:,1), record(:,2)], record(:,3))
But the results were not what I expected. Any advice?
Try this code for instance.
[x,y,z]=sphere(n);
surf(x,y,z);
axis equal
This code plots with 3 parameters surf the surface of a sphere. As far as I understood from your code you want to utilize the 2 parameters surf for your application.
According to surf help when utilizing 2 parameters surf:
surf(Z) and surf(Z,C) use x = 1:n and y = 1:m. In this case,
the height, Z, is a single-valued function, defined over a
geometrically rectangular grid.
where:
The color scaling is determined
by the range of C
It just doesn't look like you want to utilize C as the color scaling parameter. For better understanding, can you send the contents of record for reference?

Draw evenly-spaced height lines of a function in MATLAB

I would like to draw height lines of a function (represented by matrices, of course), using MATLAB.
I'm familiar with contour, but contour draws lines at even-spaced heights, while I would like to see lines (with height labels), in constant distance from one another when plotted.
This means that if a function grows rapidly in one area, I won't get a plot with dense height lines, but only a few lines, at evenly spaced distances.
I tried to find such an option in the contour help page, but couldn't see anything. Is there a built in function which does it?
There is no built-in function to do this (to my knowledge). You have to realize that in the general case you can't have lines that both represent iso-values and that are spaced with a fixed distance. This is only possible with plots that have special scaling properties, and again, this is not the general case.
This being said, you can imagine to approach your desired plot by using the syntax in which you specify the levels to plots:
...
contour(Z,v) draws a contour plot of matrix Z with contour lines at the data values specified in the monotonically increasing vector v.
...
So all you need is the good vector v of height values. For this we can take the classical Matlab exemple:
[X,Y,Z] = peaks;
contour(X,Y,Z,10);
axis equal
colorbar
and transform it in:
[X,Y,Z] = peaks;
[~, I] = sort(Z(:));
v = Z(I(round(linspace(1, numel(Z),10))));
contour(X,Y,Z,v);
axis equal
colorbar
The result may not be as nice as what you expected, but this is the best I can think of given that what you ask is, again, not possible.
Best,
One thing you could do is, instead of plotting the contours at equally spaces levels (this is what happens when you pass an integer to contour), to plot the contours at fixed percentiles of your data (this requires passing a vector of levels to contour):
Z = peaks(100); % generate some pretty data
nlevel = 30;
subplot(121)
contour(Z, nlevel) % spaced equally between min(Z(:)) and max(Z(:))
title('Contours at fixed height')
subplot(122)
levels = prctile(Z(:), linspace(0, 100, nlevel));
contour(Z, levels); % at given levels
title('Contours at fixed percentiles')
Result:
For the right figure, the lines have somewhat equal spacing for most of the image. Note that the spacing is only approximately equal, and it is impossible to get the equal spacing over the complete image, except in some trivial cases.

MeshGrid for Triangle Elements

I want to build a contourf plot of a certain aspect in my Plate. The plate is divided in triangle elements, which I have the coordinates (x,y) of each knot of the triangle.
So, How can I make a meshgrid for my knots so I can make my contourf plot?? I have the coordinates of everything and have the value of my function Z in each knot. (I'm a beginner in Matlab, sorry for this "basic" question)
If your goal is just to visualise the triangles then there is another way that's probably simpler and more robust (see the end of this post).
If you definitely need to generate contours then you will need to interpolate your triangular mesh over a grid. You can use the scatteredInterpolant class for this (documentation here). It takes the X and Y arguments or your triangular vertices (knots), as well as the Z values for each one and creates a 'function' that you can use to evaluate other points. Then you create a grid, interpolate your triangular mesh over the grid and you can use the results for the countour plot.
The inputs to the scatteredInterpolanthave to be linear column vectors, so you will probably need to reshape them using the(:)`notation.
So let's assume you have triangular data like this
X = [1 4; 8 9];
Y = [2 3; 4 5];
Z = [0.3 42; 16 8];
you would work out the upper and lower limits of your range first
xlimits = minmax(X(:));
ylimits = minmax(Y(:));
where the (:) notation serves to line up all the elements of X in a single column.
Then you can create a meshgrid that spans that range. You need to decide how fine that grid should be.
spacing = 1;
xqlinear = xlimits(1):spacing:xlimits(2);
yqlinear = ylimits(1):spacing:ylimits(2);
where linspace makes a vector of values starting at the first one (xlimits(1)) and ending at the third one (xlimits(2)) and separated by spacing. Experiment with this and look at the results, you'll see how it works.
These two vectors specify the grid positions in each dimension. To make an actual meshgrid-style grid you then call meshgrid on them
[XQ, YQ] = meshgrid(xqlinear, yqlinear);
this will produce two matrices of points. XQ holds the x-coordinates of every points in the grid, arranged in the same grid. YQ holds the y-coordinates. The two need to go together. Again experiment with this and look at the results, you'll see how it works.
Then you can put them all together into the interpolation:
F = scatteredInterpolant(X(:), Y(:), Z(:));
ZQ = F(XQ, YQ);
to get the interpolated values ZQ at each of your grid points. You can then send those data to contourf
contourf(XQ, YQ, ZQ);
If the contour is too blocky you will probably need to make the spacing value smaller, which will create more points in your interpolant. If you have lots of data this might cause memory issues, so be aware of that.
If your goal is just to view the triangular mesh then you might find trimesh does what you want or, depending on how your data is already represented, scatter. These will both produce 3D plots with wireframes or point clouds though so if you need contours the interpolation is the way to go.

How to make a plot of a circle with dashed coloured border in MATLAB?

So i have this code to obtain radial gravity on Earth in function of the latitude:
G=6.6e-11;
M=5.976e24;
N=1000;
r=6371000;
w=2*pi/(24*3600);
for i=1:1:360
Theta=i*pi/180;
x(i)=i;
Vi(i)=-G*M/(r*r);
Phii(i)=r*w*w*sin(Theta)*sin(Theta);
gr(i)=Vi(i)+Phii(i);
end
plot(x,gr)
And it runs well. I want to make a graph of a circle made of a border of points (representing angle (i)) that change colour according to the value of gr(I want to set ranges of values of gr so that if the value obtained falls in a specific category, the point will have a specific colour).
I'm really new to MATLAB. Is there any possible way to make this?
Thanks in advance.
Here is the basic algorithm that I would do:
Determine how many colours you want to represent in your plot.
Create a colour map that has this many points for what you want to compute.
Determine a linearly increasing vector that varies from the minimum value of gr to the maximum value of gr with as many points as you have determined in Step #2
For each point in gr:
a. Determine which point yields the closest distance of this point to the vector in Step #3
b. Use this to index which colour you want.
c. Convert your angle into Cartesian co-ordinates, then plot this point with the colour found in Step 4b.
Let's tackle each point in detail.
Step #1 - Determine how many colours you want
This is pretty simple. Just determine how many colours you want. For now, let's assume that you want 20 colours, so:
num_colours = 20;
Step #2 - Create a colour map
What we can do is create a 20 x 3 matrix where each row determines a RGB tuple that denotes the amount of red, green and blue that each colour will occupy. MATLAB has built-in colour maps that will help you facilitate this. Here are all of the available colour maps that MATLAB has:
Each colour map has a special variable where you can provide it an integer number, and it'll return this 2D matrix of as many rows as the number you have provided. Each row gives you an RGB triplet which denotes the proportion of red, green and blue respectively. This matrix varies from the beginning of the colour map (top row) to the end (bottom row). All you have to do is use any name seen in the figure I've shown you above to create a colour map of that type. For example, if you wanted to get a bones colour map of 15 points, simply do:
colour_map = bones(15);
If you wanted to get a jet colour map of 25 points, simply do:
colour_map = jet(25);
.... you get the idea right? I like hsv so let's use the HSV colour map. You can use any colour map you want, but let's just stick with HSV for the sake of this example.
As such:
colour_map = hsv(num_colours);
Step #3 - Get that linearly increasing vector
You want certain colours to map into certain ranges, which is why this step is important. Given a value in gr, we want to figure out which colour we want to choose, and all you have to do is determine which value in gr is the closest to a value in this vector in Step #3. Therefore, you can use linspace to do this for you:
bin_vector = linspace(min(gr), max(gr), num_colours);
This will create a num_colours 1D array where the beginning of this array starts at the minimum value of gr and varies up to the maximum value of gr and each value is equally spaced such that we generate a num_colours array.
Step #4 - Bring it all home
Now, for each point in gr, we need to figure out which point is the closest to that vector in Step #3, we then use this to figure out the colour we want, then we need to convert our angle into Cartesian co-ordinates, then plot this point.
For illustration purposes, I'm going to assume your radius is 1. You can figure out how to get the x and y co-ordinates by simply doing cos(theta) and sin(theta), where theta is the angle you are examining. Since your gr array has 360 slots, I'm going to assume a resolution of 1 degree per slot. Therefore, you can easily do this in a for loop. Make sure you use hold on because we are going to call plot multiple times, and we don't want to overwrite the plot each time we call plot. You want all of the points to stay in the plot.
Without further ado:
figure; %// Create blank figure
hold on; %// Remember all points
%// For each point in our array...
for idx = 1 : 360
%// Find the closest slot between gr and our vector in Step #3
[~,min_idx] = min(abs(gr(idx) - bin_vector));
%// Grab this colour
clr = colour_map(min_idx,:);
%// Plot the point with this colour
plot(cosd(idx), sind(idx), '.', 'Color', clr, 'MarkerSize', 10);
end
Take notice that cosd and sind take in degrees as the input argument while cos and sin take in radians. Also, take note that I also changed the size of the point so that it's bigger. With the above logic, and your array in gr, this is what I get:
If you want the radius to get larger, all you have to do is multiply each cosd and sind term with your radius. Therefore, you can do something like this:
radius = 2;
for idx = 1 : 360
... %// Insert colour code here
...
...
%// Now plot
plot(radius*cosd(idx), radius*sind(idx), '.', 'Color', clr, 'MarkerSize', 10);
end
Just leave the code the same, but for the plot command, just multiply each x and y value by the radius.
Minor note in efficiency
The way you're calculating your gr array is using an inefficient for loop. There are some situations (like mine above) where you need to use a for loop, but for simple computations there is no need. It's better if you vectorize its creation. Therefore, you can get rid of the for loop to calculate your gr array like so:
x = 1 : 360;
Theta = x*pi/180;
Phii = r*w*w*sin(Theta).*sin(Theta);
Vi = -G*M/(r*r);
gr = Vi + Phii;
x is simply a vector going from 1 to 360, and that's done in the first line. Also, Vi is just an array which contains a single value and if you know how operations work between a scalar and an array, you can just do an addition with this single value and it'll add every value in your array by this much. As such, there's no need to create an array for Vi. Also, take a look at how I calculated Phii. I'm using element-by-element operations as Theta is now an array. You want to create an array Phii that takes corresponding values of Theta, and applies that formula to each value in Theta to produce Phii.
Hope this helps. Good luck!

measure valley width 2d, matlab

I have a 2d image, I have locations where local minimas occurs.
I want to measure the width of the valleys "leading" to those minimas.
I need either the radii of the circles or ellipses fitted to these valley.
An example attached here, dark red lines on the peaks contours is what I wish to find.
Thanks.
I am partially extending the answer of #Lucas.
Given a threshold t I would consider the points P_m that are below t and closer to a certain point m of minimum of your f (given a characteristic scale length r).
(You said your data are noisy; to distinguish minima and talk about wells, you need to estimate such r. In your example it can be for instance r=4, i.e. half the distance between the minima).
Then you have to consider a metric for each well region P_m, say for example
metric(P_m) = .5 * mean{ maximum vertical diameter of P_m ,
maximum horizontal diameter of P_m}.
In your picture metric(P_m) = 2 for both wells.
On the whole, in terms of pseudo-code you may consider
M := set of local minima of f
for_each(minimum m in M){
P_m += {p : d(p,m) < r and f(r)<t} % say that += is the push operation in a Stack
}
radius_of_region_around(m) = metric(P_m); %
I would suggest making a list of points that describe the values at the edge of your ellipse, perhaps by finding all the points where it crosses a threshold.
above = data > threshold
apply a simple edge detector
edges = EdgeDetector(above)
find coordinates of edges
[row,col] = find(edges)
Then apply this ellipse fitter http://www.mathworks.com/matlabcentral/fileexchange/3215-fitellipse
I'm assuming here you have access to the x, y and z data and are not processing a given JPG (or so) image. Then, you can use the function contourc to your advantage:
% plot some example function
figure(1), clf, hold on
[x,y,z] = peaks;
surf(x,y,z+10,'edgecolor', 'none')
grid on, view(44,24)
% generate contour matrix. The last entry is a 2-element vector, the last
% element of which is to ensure the right algorithm gets called (so leave
% it untouched), and the first element is your threshold.
C = contourc(x(1,:), y(:,1), z, [-4 max(z(:))+1]);
% plot the selected points
plot(C(1,2:end), C(2,2:end), 'r.')
Then use this superfast ellipse fitting tool to fit an ellipse through those points and find all the parameters of the ellipse you desire.
I suggest you read help contourc and doc contourc to find out why the above works, and what else you can use it for.