Gradient colors for faces of surface - matlab

I have the following code for MATLAB:
close all
clear all
clc
edges= linspace(0,1,10);
[X,Y] = meshgrid(edges);
Z=rand(10);
h= surf(X,Y,Z,'FaceColor','none')
I need to paint the faces on this surface. The face with coordinate (0,0) should be green and the face with coordinate(1,1) should be red. All faces on diagonal should be yellow.
Could You help me to perform this painting?

If you have a closer look at the surf command, you'll see, that you can set a custom "colormap", which is then used instead of the Z data as a color indicator.
So, you just have to set up a proper "colormap". That one must have the same dimensions as your X and Y data, and for each data point you must specify the [R, G, B] triplet of your choice, i.e. [0, 1, 0] for the [0, 0] coordinate, [1, 0, 0] for the [1, 1] coordinate, and some "diagonal" interpolation between those two.
Luckily, you already have that, have a look at your X and Y data! Adding both will give that kind of "diagonal" interpolation for the green channel. The inverse of that will give the proper red channel. (The scaling is a bit corrupt, since you have values larger than 1.0, but these will "clipped".)
Here's the enhanced code:
edges = linspace(0, 1, 10);
[X, Y] = meshgrid(edges);
Z = rand(10);
cm(:, :, 1) = (X + Y); % Red channel
cm(:, :, 2) = 2 - cm(:, :, 1); % Green channel
cm(:, :, 3) = zeros(size(X)); % Blue channel (empty)
h = surf(X, Y, Z, cm); % No need for the FaceColor property
The output looks like this:
Hope that helps!

One way would be the following:
edges= linspace(0,1,10);
[X,Y] = meshgrid(edges);
Z=rand(10);
% Fake some color data
cdata = zeros(size(Z));
for i = 1:size(cdata,1)
cdata(i,i) = i;
end
%make a suitable colormap
cm = ones(size(cdata,1), 3);
cm = cm .*[ 1 1 0 ] % everything yellow
cm(1, :) = [1 1 1] % except for 0
cm(2, :) = [1 0 0] % except for 1
cm(end, :) = [0 1 0] % except for 10
ax = axes();
h = surface(ax, X,Y,Z);
h.CData = cdata;
ax.Colormap = cm;

Related

Visualize a 3D image in Matlab

Think of a 3 dimensional image V stored, in matlab, as an array for red, green, and blue. For pixel (i,j,k) we would have
V(i,j,k,1)=0.1 (red)
V(i,j,k,2)=0.9 (green)
V(i,j,k,3)=0.2 (blue)
I would then like to visualize this 3D image as a moving 2D slice. I thought the "slice" command could do the trick but it is designed to visualize 3D functions only, not 3D images. Do you have any suggestion?
I am adding a script that could help but does not achieve what is requested:
clear all; clc; close all;
N=35;
w=zeros(N,N,N);
x=linspace(-5,5,N); y=linspace(-5,5,N); z=linspace(-5,5,N);
for i=1:N
for j=1:N
for k=1:N
w(i,j,k)=cos(k)/(1+i+j)^2;
end
end
end
for k = 0:.1:10
hsp = surf(linspace(-5,5,Nx),linspace(-5,5,Ny),zeros(Nz));
rotate(hsp,[1,0,0],18*k,[0 0 0]) %rotate a slice where the image is shown
xd = hsp.XData;
yd = hsp.YData;
zd = hsp.ZData;
delete(hsp)
slice(x,y,z,w,xd,yd,zd)
shading flat
hold off
view(-25,20)
axis([-5 5 -5 5 -5 5]);
drawnow
end
The above w array should now be a 3D image instead.
It sounds like you want the functionality of slice but you want it to operate on an [N x M x P x 3] array instead of an [N x M x P] array.
One way to do this is by setting up an interpolator for each channel:
red = griddedInterpolant({x,y,z},V(:,:,:,1));
green = griddedInterpolant({x,y,z},V(:,:,:,2));
blue = griddedInterpolant({x,y,z},V(:,:,:,3));
and then replacing
slice(x,y,z,w,xd,yd,zd)
with
cdata = cat(3, red(xd,yd,zd), green(xd,yd,zd), blue(xd,yd,zd));
surface(xd,yd,zd,cdata);
I tried this on your example and found that the colors in w were all very close to black. So I loaded a different image to work on:
% An orange-and-black skull for Halloween
load mri
V = permute(double(D), [1 2 4 3]);
V = V / max(V(:));
V = bsxfun(#times, V, reshape([1 .3 0], [1 1 1 3]));
% Define grid coordinates and interpolator
[Nx, Ny, Nz, nCh] = size(V);
x = linspace(-5,5,Nx);
y = linspace(-5,5,Ny);
z = linspace(-5,5,Nz);
red = griddedInterpolant({x,y,z},V(:,:,:,1));
green = griddedInterpolant({x,y,z},V(:,:,:,2));
blue = griddedInterpolant({x,y,z},V(:,:,:,3));
for k = 0:.1:10
hsp = surf(linspace(-5,5,Nx),linspace(-5,5,Ny),zeros(Ny,Nx));
rotate(hsp,[1,0,0],18*k,[0 0 0])
xd = hsp.XData;
yd = hsp.YData;
zd = hsp.ZData;
delete(hsp)
cdata = cat(3, red(xd,yd,zd), green(xd,yd,zd), blue(xd,yd,zd));
surface(xd,yd,zd,cdata)
shading flat
hold off
view(-25,20)
axis([-5 5 -5 5 -5 5]);
drawnow
end

Filling an area above a curve with many colors (matlab, surf)

I'm trying to create a figure in matlab that looks like this:
desired figure
I am doing so by: (i) assigning value points to each x,y coordinate, (ii) plotting a surf, and (iii) change the view point so the third axis is not seen. Here is the code:
x = linspace(0, 1, 10);
y = linspace(0, 1, 10);
z = linspace(0, 1, 10);
z = repmat(z, 10, 1);
z = flipud(triu(z));
z(z==0) = nan;
hold off
surf(x, y, z, 'linestyle', 'none')
colormap([linspace(0.39, 1, 20)',linspace(0.58, 0.25, 20)', linspace(0.93, 0.25, 20)']);
colorbar
xlim([x(1) x(end)])
shading interp
view([90 -90])
hold on
plot(x, 1-y, 'linewidth', 2)
I get the following figure: matlab figure I get
As you can see, there a lot of white spaces above the line which I would like to be in color as well. Unfortunately, I cannot add any more grid points as calculating the actual value of the points takes a lot of time (unlike the example above).
Is there a way to have matlab draw colors in those white spaces as well?
Thanks!
You can try to use patch function to create filled polygon.
See http://www.mathworks.com/help/matlab/ref/patch.html
Try the following code:
vert = [0 1;1 1;1 0]; % x and y vertex coordinates
fac = [1 2 3]; % vertices to connect to make triangle
fvc = [1 0 0; 1 1 1; 0 0 1];
patch('Faces',fac,'Vertices',vert,'FaceVertexCData',fvc,'FaceColor','interp');
Result is close:
I was managed to get closer to the desired figure:
close all
x = linspace(0, 1, 10);
y = linspace(0, 1, 10);
%colorbar
xlim([x(1) x(end)])
%Fill rectangle.
vert = [0 0; 1 0; 1 1; 0 1]; % x and y vertex coordinates
fac = [1 2 3 4]; % vertices to connect to make squares
%patch('Faces',fac,'Vertices',vert,'FaceColor','red')
fvc = [1 0 0; 0.6 0.7 1; 0.6 0.7 1; 1 0 0]; %Color of vertices (selected to be close to example image).
patch('Faces',fac,'Vertices',vert,'FaceVertexCData',fvc,'FaceColor','interp')
hold on
%Fill lower triangle with white color.
vert = [0 0;0 1;1 0]; % x and y vertex coordinates
fac = [1 2 3]; % vertices to connect to make triangle
fvc = [1 1 1; 1, 1, 1; 1, 1, 1]; %White color
patch('Faces',fac,'Vertices',vert,'FaceVertexCData',fvc,'FaceColor','interp');
plot(x, 1-y, 'linewidth', 2)
set(gca,'Xtick',[],'Ytick',[]); %Remove tick marks
Result:
Thank you Rotem! I wasn't aware of the patch function and indeed it solved the issue!
The colors on the actual figure I'm trying to achieve are not linear, so I just used patch for all the empty triangles. Here is the adjusted code I use for the simple example (again, this is just a bit more general just to be able to have non linear colors in the area above the curve):
x = linspace(0, 1, 10);
y = linspace(0, 1, 10);
z = linspace(0, 1, 10);
z = repmat(z, 10, 1)+0.1;
z = flipud(triu(z));
z(z==0) = nan;
z = z-0.1;
hold off
surf(x, y, z, 'linestyle', 'none')
colormap([linspace(0.39, 1, 20)',linspace(0.58, 0.25, 20)', linspace(0.93, 0.25, 20)']);
colorbar
xlim([x(1) x(end)])
shading interp
view([90 -90])
hold on
patch_cor_y = kron((length(y):-1:1)', ones(3, 1));
patch_cor_x = kron((1:length(x))', ones(3, 1));
patch_cor = [y(patch_cor_y(2:end-2))', x(patch_cor_x(3:end-1))'];
patch_path = reshape(1:length(patch_cor),3, length(patch_cor)/3)';
patch_col = z(sub2ind(size(z), patch_cor_x(3:end-1), patch_cor_y(2:end-2)));
patch('Faces',patch_path,'Vertices',patch_cor,'FaceVertexCData',patch_col,'FaceColor','interp', 'EdgeColor', 'none');
plot(x, 1-y, 'linewidth', 2)
The figure achieved: figure

MATLAB: How do I shade plot area with increasing and decreasing shades of a color

I want to plot an area of a graph (x = 1:24, y = -1:1) with one color (black), which I then want to decrease/increase in shading in terms of the time of day. So, that I have a plot which is 'dark' in the background at night and 'light' during the day with x being hours of day and y being a data value. My sunrise would be at 6.8 and my sunset would be at 22. I would then overlay scatter plots with data on top.
I have tried messing around with patch and area but with no luck. Here is some code I got from elsewhere on the internet but I'm not sure how to proceed:
% Define x, f(x), and M(x)
x = linspace(6.8, 22)';
f = sin(x)+cos(x);
M = x.^2;
% Define the vertices: the points at (x, f(x)) and (x, 0)
N = length(x);
verts = [x(:), f(:), x(:) zeros(N,1)];
% Define the faces to connect each adjacent f(x) and the corresponding points at y = 0.
q = (1:N-1)';
faces = [q, q+1, q+N+1, q+N];
p = patch('Faces', faces, 'Vertices', verts, 'FaceVertexCData', [M(:); M(:)], 'FaceColor', 'interp', 'EdgeColor', 'none')
So I want the end result to be similar to the image attached below (note faint shading - I would like the gradient stronger), but with x = 1:24 and y = -1:1.
Since you ignored my request to include the results of your MATLAB code in your question, I had to look into my crystal ball to determine that your "messing around" resulted in the following error message:
Error using patch
While setting the 'Vertices' property of Patch:
Value must be a 1x2 or 1x3 vector of numeric type
Error in X (line 13)
p = patch('Faces', faces, 'Vertices', verts, 'FaceVertexCData', [M(:); M(:)], 'FaceColor', 'interp', 'EdgeColor', 'none')
This can be resolved by concatenating the vertices vertically instead of horizontally in line 8:
verts = [x(:), f(:); x(:), zeros(N,1)];
This yields the following plot:
This is of course not what you want.
Actual Solution
You can build up your background from 5 rectangles:
x = [0;
6; % 'First daylight'
6.8; % Full brightness
22; % Beginning of 'sunset'
22.8; % Complete darkness
24];
vertices = [repmat(x,2,1), [ones(size(x)); -1.*ones(size(x))]];
faces = [1:5; 2:6; 8:12; 7:11].';
color = repmat([0 0 0; % 'Night' color
0 0 0;
1 1 1; % 'Day' color
1 1 1;
0 0 0;
0 0 0],2,1);
patch('Faces',faces, ...
'Vertices',vertices, ...
'FaceVertexCData',color, ...
'FaceColor','interp', ...
'EdgeColor','red');
xlim([0 24]);
You make the vertices of the 'night' rectangles black and the vertices of the 'day' rectangle white by assigning the appropriate RGB values to the FaceVertexCData property.
The color of 'sunrise'/'sunset' rectangles is then interpolated between black and white by setting the FaceColor property to 'interp'.
The EdgeColor has only been set to 'red' to illustrate how the background is constructed. Set the property to 'interp' to make the lines disappear.

Change color of 2D plot line depending on 3rd value

I have a data set that looks like this
140400 70.7850 1
140401 70.7923 2
140402 70.7993 3
140403 70.8067 4
140404 70.8139 5
140405 70.8212 3
Where the first column corresponds to time (one second intervals between data points) and will be on the x axis, the second column corresponds with distance and will be on the y axis. The third column is a number (one through five) that is a qualification of the movement.
I want to make a plot that changes the color of the line between two points depending on what the number of the previous data point was. For example, I want the line to be red between the first and second data points because the qualification value was 1.
I've seen a lot of posts about making a sliding scale of colors depending on an intensity value, but I just want 5 colors: (red, orange, yellow, green, and blue) respectively.
I tried doing something like this:
plot(x,y,{'r','o','y','g','b'})
But with no luck.
Any ideas of how to approach this? Without looping if possible.
You can also do it with a trick which works with Matlab version anterior to 2014b (as far back as 2009a at least).
However, is will never be as simple as you expected (unless you write a wrapper for one of the solution here you can forget about plot(x,y,{'r','o','y','g','b'})).
The trick is to use a surface instead of a line object. Surfaces benefit from their CData properties and a lot of useful features to exploit color maps and texture.
Matlab surf does not handle 1D data, it needs a matrix as input so we are going to give it by just duplicating each coordinate set (for example xx=[x,x]).
Don't worry though, the surface will stay as thin as a line, so the end result is not ugly.
%% // your data
M=[140400 70.7850 1
140401 70.7923 2
140402 70.7993 3
140403 70.8067 4
140404 70.8139 5
140405 70.8212 3];
x = M(:,1) ; %// extract "X" column
y = M(:,2) ; %// same for "Y"
c = M(:,3) ; %// extract color index for the custom colormap
%% // define your custom colormap
custom_colormap = [
1 0 0 ; ... %// red
1 .5 0 ; ... %// orange
1 1 0 ; ... %// yellow
0 1 0 ; ... %// green
0 0 1 ; ... %// blue
] ;
%% // Prepare matrix data
xx=[x x]; %// create a 2D matrix based on "X" column
yy=[y y]; %// same for Y
zz=zeros(size(xx)); %// everything in the Z=0 plane
cc =[c c] ; %// matrix for "CData"
%// draw the surface (actually a line)
hs=surf(xx,yy,zz,cc,'EdgeColor','interp','FaceColor','none','Marker','o') ;
colormap(custom_colormap) ; %// assign the colormap
shading flat %// so each line segment has a plain color
view(2) %// view(0,90) %// set view in X-Y plane
colorbar
will get you:
As an example of a more general case:
x=linspace(0,2*pi);
y=sin(x) ;
xx=[x;x];
yy=[y;y];
zz=zeros(size(xx));
hs=surf(xx,yy,zz,yy,'EdgeColor','interp') %// color binded to "y" values
colormap('hsv')
view(2) %// view(0,90)
will give you a sine wave with the color associated to the y value:
Do you have Matlab R2014b or higher?
Then you could use some undocumented features introduced by Yair Altman:
n = 100;
x = linspace(-10,10,n); y = x.^2;
p = plot(x,y,'r', 'LineWidth',5);
%// modified jet-colormap
cd = [uint8(jet(n)*255) uint8(ones(n,1))].' %'
drawnow
set(p.Edge, 'ColorBinding','interpolated', 'ColorData',cd)
My desired effect was achieved below (simplified):
indices(1).index = find( data( 1 : end - 1, 3) == 1);
indices(1).color = [1 0 0];
indices(2).index = find( data( 1 : end - 1, 3) == 2 | ...
data( 1 : end - 1, 3) == 3);
indices(2).color = [1 1 0];
indices(3).index = find( data( 1 : end - 1, 3) == 4 | ...
data( 1 : end - 1, 3) == 5);
indices(3).color = [0 1 0];
indices(4).index = find( data( 1 : end - 1, 3) == 10);
indices(4).color = [0 0 0];
indices(5).index = find( data( 1 : end - 1, 3) == 15);
indices(5).color = [0 0 1];
% Loop through the locations of the values and plot their data points
% together (This will save time vs. plotting each line segment
% individually.)
for iii = 1 : size(indices,2)
% Store locations of the value we are looking to plot
curindex = indices(iii).index;
% Get color that corresponds to that value
color = indices(iii).color;
% Create X and Y that will go into plot, This will make the line
% segment from P1 to P2 have the color that corresponds with P1
x = [data(curindex, 1), data(curindex + 1, 1)]';
y = [data(curindex, 2), data(curindex + 1, 2)]';
% Plot the line segments
hold on
plot(x,y,'Color',color,'LineWidth',lineWidth1)
end
When the result figure of two variables plotted is a circle, will be necessary to add the time in z axes.
For example the figure of induction machine rotor velocity vs electric torque in one laboratory test is: 2d plot figure
In the last figure the direction of the time point plotting could be clockwise or counter clockwise. For the last reason will be added time in z axis.
% Wr vs Te
x = logsout.getElement( 'Wr' ).Values.Data;
y = logsout.getElement( '<Te>' ).Values.Data;
z = logsout.getElement( '<Te>' ).Values.Time;
% % adapt variables for use surf function
xx = zeros( length( x ) ,2 );
yy = zeros( length( y ) ,2 );
zz = zeros( length( z ) ,2 );
xx (:,1) = x; xx (:,2) = x;
yy (:,1) = y; yy (:,2) = y;
zz (:,1) = z; zz (:,2) = z;
% % figure(1) 2D plot
figure (1)
hs = surf(xx,yy,zz,yy,'EdgeColor','interp') %// color binded to "y" values
colormap('hsv')
view(2)
% %
figure(2)
hs = surf(xx,yy,zz,yy,'EdgeColor','interp') %// color binded to "y" values
colormap('hsv')
view(3)
Finally we can view the 3d form and detect that counterwise is the real direction of the time plotting is: 3d plot
Scatter can plot the color according to the value and shows the colormap of the range of values. It's hard to interpolate the color though if you want continuous curves.
Try:
figure
i = 1:20;
t = 1:20;
c = rand(1, 20) * 10;
scatter(i, t, [], c, 's', 'filled')
colormap(jet)
The figure looks like

normal to plane & closest point on a plane to the origin - matlab

I am trying to find a point on the plane that is closest to the origin. When I plot the normal, somehow it is not perpendicular to the plane! Also, the point on the plane closest to origin does not appear correct from the plot. I cannot figure out what is wrong. Any ideas?
c = 2;
x1 = [1, 0, 0] * c;
x2 = [0, 1, 0] * c;
x3 = [0, 0, 1] * c;
x = [x1(1), x2(1), x3(1)];
y = [x1(2), x2(2), x3(2)];
z = [x1(3), x2(3), x3(3)];
figure(1); plot3(x,y,z,'*r'); hold on; grid on;
normal = cross(x1-x2, x1-x3);
% Find all coefficients of plane equation
D = -dot(normal, x1);
range = [-10 10]; [X, Z] = meshgrid(range, range);
Y = (-normal(1) * X - normal(3) * Z - D)/ normal(2);
order = [1 2 4 3]; patch(X(order),Y(order),Z(order),'b');
alpha(0.3);
plot3([x(1), x(3)], [y(1), y(3)], [z(1), z(3)]);
plot3([x(1), x(2)], [y(1), y(2)], [z(1), z(2)]);
x1=x1-x3;
plot3([normal(1), x1(1)], [normal(2), x1(2)], [normal(3), x1(3)], 'k');
%% Get the point on the plane closest point to (0,0,0)
p = normal * (-D / sum(normal.^2,2));
plot3(p(1), p(2), p(3), '*g');
I appreciate your help.
normalize your normal:
normal = normal /norm(normal);
and plot the normal correctly:
plot3([x1(1)+normal(1), x1(1)], [x1(2)+normal(2), x1(2)], [x1(3)+normal(3), x1(3)], 'k');
and for a proper visualization, the axis should be of equal scale:
axis equal
Does not look bad, does it?