Matlab: using scatter3 to draw horizontal squares - matlab

h1 = scatter3(X_Horiz,Y_Horiz,Z_Horiz,200,'s','filled',...
'MarkerEdgeColor','b','MarkerFaceColor',[0 .75 .75]);
hold on;
h2 = scatter3(X_Vert,Y_Vert,Z_Vert,200,'s','filled',...
'MarkerEdgeColor','g','MarkerFaceColor',[0 .75 .75]);
hold on;
I know scatter3 can draw vertical square, however, I want to draw horizontal square which is parallel with the light red two interfaces. I try the rotate function, but it does not work.

You can proceed, by drawing your own squares by specifying the length of side. You may follow something like below:
function myscatter3()
N = 10 ;
data = rand(N,2) ;
x = data(:,1) ; y = data(:,2) ; z = zeros(size(x)) ;
dx = 0.05 ;
figure
hold on
for i = 1:N
coor = MakeSquare(x(i),y(i),dx) ;
patch(coor(:,1),coor(:,2),coor(:,3),'w','edgecolor','k') ;
end
view(3)
end
function coor = MakeSquare(x,y,dx)
coor = zeros(4,3) ;
coor(1,:) = [x-dx/2,y-dx/2,0] ;
coor(2,:) = [x+dx/2,y-dx/2,0] ;
coor(3,:) = [x+dx/2,y+dx/2,0] ;
coor(4,:) = [x-dx/2,y+dx/2,0] ;
end
Note that, that code can be fine tuned. It draws squares in the xy plane. It can be generalized to any plane.

Related

How to plot 3D path data as a ribbon in Matlab

I have scatter data X1,Y1,Z1 in 3D, which I can plot as
a=1; c=1; t=0:100;
X1 = (a*t/2*pi*c).*sin(t);
Y1 = (a*t/2*pi*c).*cos(t);
Z1 = t/(2*pi*c);
scatter3(X1,Y1,Z1);
% or plot3(X1,Y1,Z1);
The points define a 3D path. How do I make this into a ribbon plot, similar to the one below?
With delaunay triangulation I can plot it as a surface:
tri = delaunay(X1,Y1);
h = trisurf(tri, X1, Y1, Z1);
But ribbon does not give the desired result:
ribbon(Y1)
The figure below shows what I am after.
The ribbon function can only accept 2D inputs because it uses the 3rd dimension to 'build' the ribbon.
One way to achieve a 3D ribbon is to build series of patch or surface between each point and orient them properly so they look continuous.
The following code will build a ribbon around any arbitrary 3D path defined by an (x,y,z) vector. I will not explain each line of the code but there are plenty of comments and I stopped for intermediate visualisations so you can understand how it is constructed.
%% Input data
a=1; c=1; t=0:.1:100;
x = (a*t/2*pi*c).*sin(t);
y = (a*t/2*pi*c).*cos(t);
z = t/(2*pi*c);
nPts = numel(x) ;
%% display 3D path only
figure;
h.line = plot3(x,y,z,'k','linewidth',2,'Marker','none');
hold on
xlabel('X')
ylabel('Y')
zlabel('Z')
%% Define options
width = ones(size(x)) * .4 ;
% define surface and patch display options (FaceAlpha etc ...), for later
surfoptions = {'FaceAlpha',0.8 , 'EdgeColor','k' , 'EdgeAlpha',0.8 , 'DiffuseStrength',1 , 'AmbientStrength',1 } ;
%% get the gradient at each point of the curve
Gx = diff([x,x(1)]).' ;
Gy = diff([y,y(1)]).' ;
Gz = diff([z,z(1)]).' ;
% get the middle gradient between 2 segments (optional, just for better rendering if low number of points)
G = [ (Gx+circshift(Gx,1))./2 (Gy+circshift(Gy,1))./2 (Gz+circshift(Gz,1))./2] ;
%% get the angles (azimuth, elevation) of each plane normal to the curve
ux = [1 0 0] ;
uy = [0 1 0] ;
uz = [0 0 1] ;
for k = nPts:-1:1 % running the loop in reverse does automatic preallocation
a = G(k,:) ./ norm(G(k,:)) ;
angx(k) = atan2( norm(cross(a,ux)) , dot(a,ux)) ;
angy(k) = atan2( norm(cross(a,uy)) , dot(a,uy)) ;
angz(k) = atan2( norm(cross(a,uz)) , dot(a,uz)) ;
[az(k),el(k)] = cart2sph( a(1) , a(2) , a(3) ) ;
end
% compensate for poor choice of initial cross section plane
az = az + pi/2 ;
el = pi/2 - el ;
%% define basic ribbon element
npRib = 2 ;
xd = [ 0 0] ;
yd = [-1 1] ;
zd = [ 0 0] ;
%% Generate coordinates for each cross section
cRibX = zeros( nPts , npRib ) ;
cRibY = zeros( nPts , npRib ) ;
cRibZ = zeros( nPts , npRib ) ;
cRibC = zeros( nPts , npRib ) ;
for ip = 1:nPts
% cross section coordinates.
csTemp = [ ( width(ip) .* xd ) ; ... %// X coordinates
( width(ip) .* yd ) ; ... %// Y coordinates
zd ] ; %// Z coordinates
%// rotate the cross section (around X axis, around origin)
elev = el(ip) ;
Rmat = [ 1 0 0 ; ...
0 cos(elev) -sin(elev) ; ...
0 sin(elev) cos(elev) ] ;
csTemp = Rmat * csTemp ;
%// do the same again to orient the azimuth (around Z axis)
azi = az(ip) ;
Rmat = [ cos(azi) -sin(azi) 0 ; ...
sin(azi) cos(azi) 0 ; ...
0 0 1 ] ;
csTemp = Rmat * csTemp ;
%// translate each cross section where it should be and store in global coordinate vector
cRibX(ip,:) = csTemp(1,:) + x(ip) ;
cRibY(ip,:) = csTemp(2,:) + y(ip) ;
cRibZ(ip,:) = csTemp(3,:) + z(ip) ;
end
%% Display the full ribbon
hd.cyl = surf( cRibX , cRibY , cRibZ , cRibC ) ;
set( hd.cyl , surfoptions{:} )
Now you have your graphic object contained in one surface object, you can set the options for the final rendering. For example (only an example, explore the surface object properties to find all te possibilities).
%% Final render
h.line.Visible = 'off' ;
surfoptionsfinal = {'FaceAlpha',0.8 , 'EdgeColor','none' , 'DiffuseStrength',1 , 'AmbientStrength',1 } ;
set( hd.cyl , surfoptionsfinal{:} )
axis off
Note that this code is an adaptation (simplification) of the code provided in this answer (to that question: Matlab: “X-Ray” plot line through patch).
This method allows to draw an arbitrary cross section (a disc in the answer) and build a surface which will follow a path. For your question I replaced the disc cross section by a simple line. You could also replace it with any arbitrary cross section (a disc, a square, a potatoid ... the sky is the limit).
Edit
Alternative Method:
Well it turns out there is a Matlab function which can do that. I first discarded it because it is meant for 3D volume visualisations, and most ways to call it require gridded input (meshgrid style). Luckily for us, there is also a calling syntax which can work with your data.
% Same input data
a=1; c=1; t=0:.1:100;
x = (a*t/2*pi*c).*sin(t);
y = (a*t/2*pi*c).*cos(t);
z = t/(2*pi*c);
% Define vertices (and place in cell array)
verts = {[x.',y.',z.']};
% Define "twistangle". We do not need to twist it in that direction but the
% function needs this input so filling it with '0'
twistangle = {zeros(size(x.'))} ;
% call 'streamribbon', the 3rd argument is the width of the ribbon.
hs = streamribbon(verts,tw,0.4) ;
% improve rendering
view(25,9)
axis off
shading interp;
camlight
lighting gouraud
Will render the following figure:
For additional graphic control (over the edges of the ribbon), you can refer to this question and my answer: MATLAB streamribbon edge color

Plot bivariate map in Matlab

I have two maps (matrices), and want to use them to generate the corresponding bivariate map.
The below code illustrates the idea using 'scatter', but because my original matrices are quite large, I need a different solution than to draw each point individually.
red_blue_colormap = brewermap(20,'RdYlBu'); %using ColorBrewer colormaps 'brewermap'
white2red = newmap(10:-1:1,:); %color map going from white to red
white2blue = newmap(11:20,:); %color map going from white to blue
X = rand(13,8); Y = rand(13,8); %sample matrices
figure;
for i=1:size(X,1)
for j=1:size(X,2)
portion_red = white2red(ceil(X(i,j)*10),:); %value between 'white' and 'red' corresponding to value of X(i,j)
portion_blue = white2blue(ceil(Y(i,j)*10),:);
subplot(2,2,1); hold on; title('X');
scatter(i,j,100,portion_red ,'s','filled');
subplot(2,2,2); hold on; title('Y');
scatter(i,j,100,portion_blue ,'s','filled');
subplot(2,2,3); hold on; title('X vs. Y');
w1 = Y(i,j)/(X(i,j) + Y(i,j)); %relative weight of 'Y'
color1 = portion_red - ((portion_red - portion_blue ) * w1);
scatter(i,j,100,color1,'s','filled');
subplot(2,2,4); hold on; title('Color matrix');
portion_red = white2red(ceil(i/size(X,1)*10),:);
portion_blue = white2blue(ceil(j/size(X,2)*10),:);
w2 = (j/size(X,2))/(i/size(X,1) + j/size(X,2));
color2 = portion_red - ((portion_red - portion_blue ) * w2);
scatter(i,j,100,color2,'s','filled');
end
end
This generates the following figure: https://image.ibb.co/kotP7m/bivariate_map.png
I want to plot the bottom left figure in a more efficient way. Any ideas?

Radon Transform Line Detection

I'm trying to detect lines in a grayscale image. For that purpose, I'm using Radon transform in MATLAB. An example of my m-file is like below. I can detect multiple lines using this code. I also draw lines using shift and rotation properties for lines. However, I didn't understand how to get the start and end points of the detecting lines after getting rho and theta values.
It is easy for Hough transform since there is a function called houghlines() that returns the list of the lines for the given peaks. Is there any function that i can use for Radon transform similar to this function?
% Radon transform line detection algorithm
clear all; close all;
% Determine the path of the input image
str_inputimg = '3_lines.png' ;
% Read input image
I = imread(str_inputimg) ;
% If the input image is RGB or indexed color, convert it to grayscale
img_colortype = getfield(imfinfo(str_inputimg), 'ColorType') ;
switch img_colortype
case 'truecolor'
I = rgb2gray(I) ;
case 'indexedcolor'
I = ind2gray(I) ;
end
figure;
subplot(2,2,1) ;
imshow(I) ;
title('Original Image') ;
% Convert image to black white
%BW = edge(I,'Sobel');
BW=im2bw(I,0.25) ;
subplot(2,2,2) ;
imshow(BW);
title('BW Image') ;
% Radon transform
% Angle projections
theta = [0:179]' ;
[R, rho] = radon(BW, theta) ;
subplot(2,2,3) ;
imshow(R, [], 'XData', theta, 'YData', rho, 'InitialMagnification', 'fit');
xlabel('\theta'), ylabel('\rho');
axis on, axis normal, hold on;
% Detect the peaks of transform output
% Threshold value for peak detection
threshold_val = ceil(0.3*max(R(:))) ;
% Maximum nof peaks to identify in the image
max_nofpeaks = 5 ;
max_indexes = find(R(:)>threshold_val) ;
max_values = R(max_indexes) ;
[sorted_max, temp_indexes] = sort(max_values, 'descend') ;
sorted_indexes = max_indexes(temp_indexes) ;
% Get the first highest peaks for the sorted array
if (length(sorted_max) <= max_nofpeaks)
peak_values = sorted_max(1:end) ;
peak_indexes = sorted_indexes(1:end) ;
else
peak_values = sorted_max(1:max_nofpeaks) ;
peak_indexes = sorted_indexes(1:max_nofpeaks) ;
end
[y, x] = ind2sub(size(R), peak_indexes ) ;
peaks = [rho(y) theta(x)] ;
plot(peaks(:,2), peaks(:,1), 's', 'color','white');
title('Radon Transform & Peaks') ;
% Detected lines on the image
subplot(2,2,4), imshow(I), title('Detected lines'), hold on
x_center = floor(size(I, 2)/2) ;
y_center = floor(size(I, 1)/2) ;
for p=1:length(peaks)
x_1 = [-x_center, x_center] ;
y_1 = [0, 0] ;
% Shift at first
x_1_shifted = x_1 ;
y_1_shifted = [y_1(1)-peaks(p,1), y_1(2)-peaks(p,1)] ;
% Rotate
peaks(p,2) = 90 - peaks(p,2) ;
t=peaks(p,2)*pi/180;
rotation_mat = [ cos(t) -sin(t) ; sin(t) cos(t) ] ;
x_y_rotated = rotation_mat*[x_1_shifted; y_1_shifted] ;
x_rotated = x_y_rotated(1,:) ;
y_rotated = x_y_rotated(2,:) ;
plot( x_rotated+x_center, y_rotated+y_center, 'b', 'linewidth', 2 );
end
hold off;
There's a suggestion at math.SE which might help. Then there's a rather complicated-looking research paper "Sharp endpoint estimates for the X-ray transform and the Radon
transform in finite fields", which appears just to show certain bounds on estimation accuracy.
From skimming other papers, it appears that it's a nontrivial problem. I suspect it may be simpler (if less accurate) to use some adaptation of a Sobel-operation to identify high gradient points along the discovered line, and claim those as endpoints.

3d Ring in Matlab using Patch

I am trying to make a 3d ring in a matlab. Following is something similar to what I am upto:
t = linspace(0,2*pi);
rin = 0.1;
rout = 0.25;
xin = 0.5 + rin*cos(t);% 0.5 is the center of the ring
xout = 0.5 + rout*cos(t);
yin = 0.5 + rin*sin(t);
yout = 0.5 + rout*sin(t);
% Make patch
hp = patch([xout,xin],[yout,yin],'g','linestyle','none','facealpha',0.25);
Now, I want to extend this to the 3d case. I wanted to give height to this ring. But when I try to add a vector [z1,z2] where
z1=0.5*ones(size(xin));
z2=z1;
I have tried different combination of Z1 and Z2 still I am unable to find the solution.
I tend to keep the usage of patch for 2D surfaces of object which are really faceted and if you need control over the properties of every face, otherwise for volumes like you want surf is a much easier object to handle.
The example below shows how to do it with one single surface, but is easily adaptable if you really need to use patch.
To explain step by step I start with the patch you created. I calculate the coordinates of the center line (called ring in the code), then display it (with the starting point highlighted)
%% // Ring properties
ring.x0 = 0.5 ; %// Center of ring
ring.y0 = 0.5 ; %// Center of ring
ring.radius = (0.25+0.1)/2 ; %// Radius of core circle profile
ring.nDiv = 37 ; %// number of divisions for the core circle profile
ring.theta = linspace(0,2*pi,ring.nDiv) ;
ring.X = cos(ring.theta) * ring.radius + ring.x0 ;
ring.Y = sin(ring.theta) * ring.radius + ring.y0 ;
%// plot (optional, just for intermediate visualisation)
hold on ; plot(ring.X,ring.Y) ; plot(ring.X(1),ring.Y(1),'ok')
view(18,72) ; xlabel('X') ; ylabel('Y') ; zlabel('Z') ;
which render:
Then I create a basic cross section:
%% // Create a base SQUARE cross section
Npts = 4 ;
cs.width = 0.25-0.1 ; %// width of each cross section square
cs.height = 0.25 ; %// height of each cross section square
%// first cross section is the the XZ plane
csY0 = zeros(1,Npts) ; %// will be used as base for rotating cross sections
csX = [-cs.width/2 cs.width/2 cs.width/2 -cs.width/2 ] ;
csZ = [-cs.height/2 -cs.height/2 cs.height/2 cs.height/2] ;
This defined a basic square hanging in space around the origin, I'll place the first one in position just to illustrate:
%% // plot (optional, just for intermediate visualisation)
hp0 = patch(csX+ring.X(1),csY0+ring.Y(1),csZ,'r','FaceAlpha',0.5) ;
view(164,38)
Which renders:
Now we just need to replicate this cross section wrapped around the master ring:
%% Generate coordinates for each cross section and merge them
nCS = length(ring.X) ; %// number of cross sections composing the surface
%// pre-allocation is always good
X = zeros( nCS , Npts ) ;
Y = zeros( nCS , Npts ) ;
Z = zeros( nCS , Npts ) ;
for ip = 1:nCS
%// rotate the cross section (around Z axis, around origin)
Rmat = [ cos(ring.theta(ip)) -sin(ring.theta(ip)) ; ...
sin(ring.theta(ip)) cos(ring.theta(ip)) ] ;
csTemp = Rmat * [csX ; csY0] ;
%// translate the coordinates of cross section to final position and store with others
X(ip,:) = csTemp(1,:) + ring.X(ip) ;
Y(ip,:) = csTemp(2,:) + ring.Y(ip) ;
Z(ip,:) = csZ ;
end
Now you have in X,Y and Z the coordinates of points all around the profile you defined, ready to be plotted in one graphical object:
%% // Plot the final surface
hs = surf(X,Y,Z) ;
set(hs,'FaceColor',[.7 .7 .7],'FaceAlpha',0.5,'EdgeAlpha',0.2)
view(155,26)
Which renders:
The 2 nice points about this method are:
Only one graphic object to handle (still versatile though, the CData allows many possibilities)
The cross section can be anything, just define it once, and repeat the method.
To illustrate point 2 above, just replace the paragraph of code %% // Create a base square cross section by this circular cross section:
%% // Create a base CIRCULAR cross section
cs.Ndiv = 13 ; %
cs.radius = (0.25-0.1)/2 ; %// Radius of each cross section circle
cs.rout = 0.25;
cs.theta = linspace(0,2*pi,cs.Ndiv) ;
Npts = length(cs.theta) ;
%// first cross section is the the XZ plane
csY0 = zeros(1,Npts) ; %// will be used as base for rotating cross sections
csX = sin(cs.theta) * cs.radius ;
csZ = cos(cs.theta) * cs.radius ;
The rest of the code is the same, you'll obtain your doughnut:
I included that anyway because that was my first answer, until I realized from the comment that you wanted a cylinder!
Duplicating your annulus and adding two cylinders i get:
%% Config
t = linspace(0,2*pi);
rin = 0.1;
rout = 0.25;
center = [1, 0.5];
xin = rin*cos(t);
xout = rout*cos(t);
yin = rin*sin(t);
yout = rout*sin(t);
z1 = 0;
z2 = 0.24;
%% Plot
clf;
hold on;
bottom = patch(center(1)+[xout,xin], ...
center(2)+[yout,yin], ...
z1*ones(1,2*length(xout)),'');
top = patch(center(1)+[xout,xin], ...
center(2)+[yout,yin], ...
z2*ones(1,2*length(xout)),'');
[X,Y,Z] = cylinder(1,length(xin));
outer = surf(rout*X+center(1), ...
rout*Y+center(2), ...
Z*(z2-z1)+z1);
inner = surf(rin*X+center(1), ...
rin*Y+center(2), ...
Z*(z2-z1)+z1);
set([bottom, top, outer, inner], ...
'FaceColor', [0 1 0], ...
'FaceAlpha', 0.99, ...
'linestyle', 'none', ...
'SpecularStrength', 0.7);
light('Position',[1 3 2]);
light('Position',[-3 -1 3]);
axis vis3d; axis equal; view(3);

Bending a plane into a closed surface/cylinder

I'm doing a mathematical experiment in Matlab and the result should be a circle in the x,y-plane. But sometimes, the circle starts spiraling. I'm now trying to bend the x,y-plane into a cylinder (as in the following picture). At the moment I only have the x and y coordinates of the points.
I've tried converting them into polar coordinates and then use some 'surf' commando's, but nothing works right now
(source: wtcoeselgem.be)
Edit: I've used the plot3 command, as suggested by Ander Biguri, resulting in the following figure.
(source: wtcoeselgem.be)
I will consider that you have a curve defined by x and y coordinates which you want to fold around a cylinder.
%% // Generate sample data
x = linspace(0,10*pi) ;
y2 = cos(x) ;
y1 = 10*cos(x/10) ;
y = y1+y2 ; y = y-min(y) ;
figure, plot(x,y,'-o') ;
This produces:
Next I define a basic cylinder, nothing original:
%% // Basic cylinder (just for background)
[Xc,Yc,Zc] = cylinder(1,100);
Zc = Zc * max(y) ;
hs = surf(Xc,Yc,Zc) ;
set(hs,'FaceColor',[.8 .8 .8],'FaceAlpha',0.5,'EdgeColor','none') ;
hold on
And here comes the interesting bit:
%% // Fold the points around the cylinder
Number_of_turn = 2 ;
xrange = [min(x),max(x)] ;
xspan = xrange(2)-xrange(1) ;
xc = x / xspan * 2*pi * Number_of_turn ;
Xp = cos(xc) ;
Zp = y ;
Yp = sin(xc) ;
hp = plot3(Xp,Yp,Zp,'-ok') ;
Which render:
For this example I assumed you wanted to wrap your curve around "2 turns" of the cylinder. This is easily changed with the Number_of_turn variable.
Note that you can also change the radius of the cylinder by multiplying the Xp and Yp coordinates by your radius.
The following seems to do more or less what you want
%// Data
xmin = -3;
xmax = 3; %// this piece will get folded into a cylinder
Rc = 5; %// cylinder radius
zmaxc = 5; %// cylinder max z
zminc = -5; %// cylinder min z
%// Spiral
t = linspace(0,1,1000);
r = 1+2*t;
theta = 2*pi*3*t;
x1 = r.*cos(theta);
y1 = r.*sin(theta); %// example spiral. Defined by x1, y1
%// Do the bending
z2 = y1;
phi = (x1-xmin)/(xmax-xmin)*2*pi;
x2 = Rc*cos(phi);
y2 = Rc*sin(phi);
%// Plot cylinder
[xc yc zc] = cylinder(Rc*ones(1,100),100);
zc = zminc + (zmaxc-zminc)*zc;
surf(xc,yc,zc)
shading flat
hold on
%// Plot bent spiral
plot3(x2,y2,z2, 'k.-');
Original spiral:
Two views of the result: