2D Plotting a 3D point cloud as set of lines - matlab

I have a three dimensional array of points.
I need to plot them on a 2D [x,z] grid, where every line is based on the value of Y inside a range.
es. the first line is made by points with 0 < y < 1, the second with 1 < y < 2, etc..
I can plot points just fine using this script(to have a better looking graph i'm also changing the color for every set of point by moving inside an rgb triplet).
set is the range of the line (in my case Y is time and i need to plot every 0.1 seconds). i is the index for my array of points.
w= 0;
yend = 100;
set = 0.1;
numberOfColors = 1/2*(Yinterval)*(1/set);
incrementOfColor = 1/numberOfColors;
red = 0;
green = 0;
blue = 1;
color=[red green blue];
while w < yend
while y(i)>w&&y(i)<w+set
figure(1)
hold on
plot(x(i),z(i),'.','Color',color);
hold on
i=i+1;
end
w=w+set;
if red < 1-incrementOfColor && blue > 0
red = red + incrementOfColor;
blue = blue - incrementOfColor;
end
if red > incrementOfColor && blue < incrementOfColor
red = red-incrementOfColor;
green = green + incrementOfColor;
blue = 0;
end
color = [red green blue];
end
And this is the result:
http://i.imgur.com/HTyzWai.png
When there's too much data the plot becomes cumbersome to read, and with too little data isolated points don't really tell much.
I've tried converting the points inside a "set" interval into an array but it really didn't work out:
while w < yend
while y(i)>w&&y(i)<w+set
vX(a,:)=x(i);
vZ(a,:)=z(i);
i=i+1;
a=a+1;
end
figure(2)
hold on
plot(vX,vZ,'-','Color',color);
Gave this result: imgur.com/S7OasUn.jpg
Is my conversion wrong or arrays are just not the proper way to handle this? If so, what should I use?
I'm rather new to matlab so if this is really obvious or i'm asking something in the wrong way, please excuse me.
edit:
This is the result i want to achieve:
imgur.com/jPZTO8E.png
and it was obtained with Origin's contour profile tool.
I guess you mean this with functional example:
myPoints[i]:
i=0, x = 1, y= 0.03, z = 1
i=1, x = 2, y= 0.06, z = 3
i=2, x = 2.5, y = 0.09, z = 4
i=3, x = 1.2, y = 1.01, z = 3.1
i=4, x = 1.3, y = 1.04, z = 1.1
i=5, x = 1.2, y = 1.06, z = 2.5
i=6, x = 2, y = 1.09, z = 3.1
i=7, x = 1.2, y = 2.02, z = 3.1
etc..
i want the points i 0,1,2 plotted as one line, i 3,4,5 as another line and i 7 etc.. plotted as a third line. In my data i have a lot of points.
this is one example of the data i'm using.
https://drive.google.com/file/d/0B9GHAkIYepQcRXdBdy03dFJtT1k/view?usp=sharing
where:
x = myPoints(:,1);
y = myPoints(:,2);
z = myPoints(:,3);
In this specific case, i only care about the points with y value between 85.85 and 90.85, and i want to "place an edge" (?) every 0.1
x = myPoints(:,1);
y = myPoints(:,2);
z = myPoints(:,3);
ymin = 85.85;
ymax = 90.85;
set = 0.1;
nbins = (ymax-ymin)/set;
binedge = linspace(ymin, ymax, (nbins + 1));

I think I'm understanding what you're attempting to accomplish and I believe you can accomplish your task with a vectorized use of plot rather than having to loop through each of your points.
Given your example data we can give this method a shot:
load('mypoints.mat');
x = myPoints(:,1);
y = myPoints(:,2);
z = myPoints(:,3);
% Bin your data by y values
ymin = 85.85;
ymax = 90.85;
set = 0.1;
nbins = (ymax-ymin)/set;
binedge = linspace(ymin, ymax, (nbins + 1));
[~, ~, binidx] = histcounts(y, binedge);
binloop = unique(binidx);
% Fix for error with unique. If you try to use a for loop with a column
% vector it passes the whole vector as your iterator, so you need to
% transpose it to a row vector.
temp = size(binloop);
if temp(1) ~= 1
binloop = binloop';
end
% Plot
figure
hold on
for ii = binloop
if ii == 0
% These fall outside of our bins, don't plot them
continue
else
dataidx = find(binidx == ii);
% Plot has its own color cycle, or you can add your color changing logic to this loop
plot(x(dataidx), z(dataidx), '-', 'LineWidth', 0.25);
end
end
hold off
What I've done here is to utilize the third output of histcounts to bin your y data and return a bin index for each value in your y vector.
To break the code down a bit:
Define your bin boundaries:
ymin = 85.85;
ymax = 90.85;
set = 0.1;
nbins = (ymax-ymin)/set;
binedge = linspace(ymin, ymax, (nbins + 1));
The linspace call returns the edges of each data bin. Given your example data we get binedge = [86.85, 86.95, 86.05, ..., 90.85]. We then plug in these edges and your y vector to histcounts to obtain our bin indices:
[~, ~, binidx] = histcounts(y, binedge);
Where the bins are defined per the documentation:
The value X(i) is in the kth bin if edges(k) ≤ X(i) < edges(k+1). The
last bin also includes the right bin edge, so that it contains X(i) if
edges(end-1) ≤ X(i) ≤ edges(end).
We can use unique to loop through all of the bins that are present. We can utilize find with MATLAB's logical indexing to plot all data from a single bin in each loop iteration. I ignore where ii == 0 as it indicates where data does not fall under one of the bins (and 0 index is meaningless in MATLAB).
I also played around a bit with the line sizes to try and make the plot a bit easier to read. There's a lot of data to visualize here but it helps a bit. You'll need to do a bit of pruning of your data since your x values wrap back, but that should be trivial.

Related

Coloring sections of sphere, some regions end up with unassigned colors

I am trying to create a spherical histogram and I have found an addon that creates a spherical histogram, however it uses equal area quadrilaterals and for my purposes I would like to preserve the lines found on the traditional sphere created with MATLAB so they can match latitude and longitude lines.
I found a way to color a given subset of the sphere by setting the min/max Azimuth and Elevation values of the region I want to shade, and to try and test out coloring each cell of the sphere I have tried dividing the azimuth and elevation total angles by a given n value and looping through to assign a random color to each cell. This works for about 2/3s of the cells, however there are random azimuth/elevation sections where there is no color assigned, indicating some type of problem with the surf() function presumably. I thought the count n for the sphere being only 20 might cause rounding errors that would be the main contributing factor for this, but there are proportionally similar sized gaps found for a 50x50 cell sphere as well. My best guess is some kind of rounding precision error that causes the given cell region to skip assigning a color due to the given bounds being too far from matching an actual cell, essentially having the given bounds being in between two lines. How can I iterate through every cell based on its azimuth and elevation range for a given sphere of size n?
n = 20;
%// Change your ranges here
minAzimuth = -180;
maxAzimuth = 180;
minElevation = 0;
maxElevation = 180;
%// Compute angles - assuming that you have already run the code for sphere
[x,y,z] = sphere(n);
theta = acosd(z);
phi = atan2d(y, x);
%%%%%// Begin highlighting logic
ind = (phi >= minAzimuth & phi <= maxAzimuth) & ...
(theta >= minElevation & theta <= maxElevation); % // Find those indices
x2 = x; y2 = y; z2 = z; %// Make a copy of the sphere co-ordinates
x2(~ind) = NaN; y2(~ind) = NaN; z2(~ind) = NaN; %// Set those out of bounds to NaN
%%%%%// Draw our original sphere and then the region we want on top
r = 1;
surf(r.*x,r.*y,r.*z,'FaceColor', 'white', 'FaceAlpha',0); %// Base sphere
hold on;
%surf(r.*x2,r.*y2,r.*z2,'FaceColor','red', 'FaceAlpha',0.5); %// Highlighted portion
%// Adjust viewing angle for better view
for countAz = 1:1:n
current_minAzimuth = -180 + (countAz-1) * (360/n);
current_maxAzimuth = -180 + (countAz) * (360/n);
for countE = 1:1:n
current_minElevation = 0 + (countE-1) * (180/n);
current_maxElevation = 0 + (countE) * (180/n);
theta = acosd(z);
phi = atan2d(y, x);
%%%%%// Begin highlighting logic
ind = (phi >= current_minAzimuth & phi <= current_maxAzimuth) & ...
(theta >= current_minElevation & theta <= current_maxElevation); % // Find those indices
x2 = x; y2 = y; z2 = z; %// Make a copy of the sphere co-ordinates
x2(~ind) = NaN; y2(~ind) = NaN; z2(~ind) = NaN;
random_color = rand(1,3);
surf(r.*x2,r.*y2,r.*z2,'FaceColor',random_color, 'FaceAlpha',1);
end
end
axis equal;
view(40,40);
Here is an n=50 sphere:
And here is an n=20 sphere
You're doing more conversions to and from spherical/Cartesian coordinates than you need to, and subsequently doing more floating point comparisons than required.
You are essentially just looping through all of the 2x2 index blocks in the x, y and z arrays.
It's simpler to just directly create the index arrays you're trying to use and then pull out those values of the existing surface coordinates.
for countAz = 1:1:n
for countE = 1:1:n
% Linear index for a 2x2 patch of the matrices
idx = countAz + [0,1,n+(1:2)] + (countE-1)*(n+1);
% Pull out the coordinates, reshape for surf
x2 = x(idx); x2 = reshape(x2,2,2);
y2 = y(idx); y2 = reshape(y2,2,2);
z2 = z(idx); z2 = reshape(z2,2,2);
random_color = rand(1,3);
surf(r*x2,r*y2,r*z2,'FaceColor',random_color, 'FaceAlpha',1);
end
end

Plotting "saw-tooth like" functions on MATLAB

I'm trying to plot a piece-wise function (of the form y = αx + β) on a single plot, such that for different regions on the x-axis, I have different values of α and β for the function.
I want to make the locations (on the x-axis) of these steps modifiable at will instead of having a predetermined number of such piece-functions. When plotted, it should ideally look like set of lines of different slopes and intercepts, each separated by a spacing. So if my spacing vector has 10 elements, I wrote my code such that I will correspondingly have 10 different piece-functions for different regions on the x-axis.
Here is the code that I wrote.
x = linspace(0,100,10000);
y = zeros(1,10000);
spacing = 0:10:100;
alpha = linspace(1,3,length(spacing)); %setting arbitrary upper lim
beta = linspace(1,5,length(spacing));
for j = 1:length(spacing)
for i=1:10000
if x(i) <= spacing(j)
y(i) = alpha(j)*x(i) + beta(j);
i = i + 1;
else
j = j + 1;
end
end
end
plot(x,y)
However, when I plot this, I get a single slope. It doesn't seem to be recognizing a change in spacing(j) due to the j = j + 1 iterator in the else statement
Any suggestions or help on how I should approach this would be much appreciated!
You should first iterate over x and then over spacing. Because for every element in x you are trying to find the correct interval. Then once you found that interval you should move to the next element of x and stop the iteration over spacing. You can do that using brake. If you don't do that then it will always choose the last interval, since x(i) <= spacing(end). See the code below:
x = linspace(0,100,10000);
y = zeros(1,10000);
spacing = 0:10:100;
alpha = linspace(1,3,length(spacing)); %setting arbitrary upper lim
beta = linspace(1,5,length(spacing));
for i=1:10000
for j = 1:length(spacing)
if x(i) <= spacing(j)
y(i) = alpha(j)*x(i) + beta(j);
break
end
end
end
plot(x,y)
ylim([0 max(y)])
The last line is to set y to start from 0.

How to generate random positions with distance between them inside the hexagon?

I am trying to create N random pairs of points (N = 50) of a given distances, inside a 500 meters hexagon. The distance D created by using (dmax - dmin).*rand(N,1) + dmin, with dmin = 10 and dmax = 100 in Matlab. I understant that the first I have to generate a set of points ([x1 y1]) that have at least distance D from the main hexagon border, then generate the second set of points ([x2 y2]) that have exact distance D from the first set. But sometime I got the problem with the second point outside of hexagon, because if the first position on the hexagol border and plus Ddisance, then the second position is outside of hexagon (I mean that I want to generate random pair position inside of hexagol). Could anybody help me in generating this kind of scenario and fix the problem? Thanks.
For example as
R = 500; % hexagol radius
N = 50; % number pair positions
d_min = 10; % minimum distance
d_max = 100; % maximum distance
D = (d_max - d_min).*rand(N,1) + d_min; % randomly distance
X = [0,0]; % hexagol center
j=0;
while j < N
j=j+1;
theta(j)=2*pi*rand(1,1);
u= rand()+ rand();
if u < 1
r(j) = R * u;
else
r(j) = R * (2 - u);
end
% to create the first position
x1(j)=r(j)*cos(theta(j)) + X(1,1); % first x positions
y1(j)=r(j)*sin(theta(j)) + X(1,2); % first y positions
end
% to create the second position
x2(j) = x1(j) + D(j); % second x positions
y2(j) = y1(j) + D(j); % second y positions
This is quite like your other question and its solution is almost the same, but it needs a little more math. Let’s focus on one pair of points. There still are two steps:
Step 1: Find a random point that is inside the hexagon, and has distance d from its border.
Step 2: Find another point that has distance d from first point.
Main problem is step 1. We can say that a points that has distance d form a hexagon with radius r, is actually inside a hexagon with radius r-d. Then we just need to find a random point that lays on a hexagon!
Polar Formula of Hexagons:
I want to solve this problem in polar space, so I have to formulate hexagons in this space. Remember circle formula in polar space:
The formula of a hexagon in polar space is pretty much like its circumscribe circle, except that the radius of the hexagon differs at every t (angle). Let’s call this changing radius r2. So, if we find the function R2 that returns r2 for all ts then we can write polar formula for hexagon:
This image demonstrates parameters of the problem:
The key parameter here is α. Now we need a function Alpha that returns α for all ts:
Now we have all points on border of the hexagon in polar space:
r = 500;
T = linspace(0, 2*pi, 181);
Alpha = #(t) pi/2-abs(rem(t, pi/3)-(pi/6));
R2 = #(t) r*cos(pi/6)./sin(Alpha(t));
X = R2(T).*cos(T);
Y = R2(T).*sin(T);
hold on
plot(X, Y, '.b');
plot((r).*cos(T), (r).*sin(T), '.r')
Polar Formula of a Regular Polygon:
Before I go on I’d like to generalize Alpha and R2 functions to cover all regular polygons:
Alpha = #(t) pi/2-abs(rem(t, 2*pi/(n))-(pi/(n)));
R2 = #(t) r*cos(pi/n)./sin(Alpha(t));
Where n is the number of edges of the polygon.
Answer:
Now we can generate pairs of points just like what we did for the circle problem:
r = 500; n = 6;
a = 10; b = 50;
N = 100;
D = (b - a).*rand(N,1) + a;
Alpha = #(t) pi/2-abs(rem(t, 2*pi/(n))-(pi/(n)));
R2 = #(t) r*cos(pi/n)./sin(Alpha(t));
T1 = rand(N, 1) * 2 * pi;
RT1 = rand(N, 1) .* (R2(T1)-D);
X1 = RT1.*cos(T1);
Y1 = RT1.*sin(T1);
T2 = rand(N, 1) * 2 * pi;
X2 = X1+D.*cos(T2);
Y2 = Y1+D.*sin(T2);
Rotating the polygon:
For rotating the polygon we just need to update the Alpha function:
t0 = pi/8;
Alpha = #(t) pi/2-abs(rem(t+t0, 2*pi/(n))-(pi/(n)));
This is a test for n=7, N=50000 and t0=pi/10:

ContourPlot and inequalitie plot together

Im new to matlab and I want to plot a ContourPlot together with two inequalities but I dont know how. The function that I want to plot its ContourPlot is something like this:
Z = (X-2).^2 + (Y-1).^2;
and here are the two inequalities:
ineq1 = X.^2 - Y <= 2;
ineq2 = X + Y <= 2;
this is what I have dodne so far:
[X,Y] = meshgrid(-4:.2:4,-4:.2:4);
Z = (X-2).^2 + (Y-1).^2;
[C,h] = contour(X,Y,Z);
clabel(C,h)
ineq1 = X.^2 - Y <= 2;
ineq2 = X + Y <= 2;
range = (-4:.2:4);
hold on
plot(range,ineq1, range, ineq2)
hold off
but this does not feel right.
What I want to do is to visualize an optimization problem. First I want to plot the ContourPlot of the function and then the possible areas in that same plot, It would be great if I could show the intersection areas too.
If you want to draw the boundaries of the inequalities onto the contour plot, you can do this with line.
[X,Y] = meshgrid(-4:.2:4,-4:.2:4);
Z = (X-2).^2 + (Y-1).^2;
[C,h] = contour(X,Y,Z);
clabel(C,h)
x_vect = (-4:.05:4);
y_ineq1 = x_vect.^2 - 2;
y_ineq2 = 2 - x_vect;
line(x_vect, y_ineq1);
line(x_vect, y_ineq2);
Coloring the intersection area is a bit more tricky, and I'm not sure if it's exactly what you want to do, but you could look into using patch, something like
% Indexes of x_vect, y_ineq1 and y_ineq2 for region where both satisfied:
region_indexes = find(y_ineq1<y_ineq2);
region_indexes_rev = region_indexes(end:-1:1);
% To plot this area need to enclose it
% (forward along y_ineq1 then back along y_ineq2)
patch([x_vect(region_indexes),x_vect(region_indexes_rev)],...
[y_ineq1(region_indexes),y_ineq2(region_indexes_rev)],...
[1 0.8 1]) % <- Color as [r g b]
% May or may not need following line depending on MATLAB version to set the yaxes
ylim([-4 4])
% Get the original plot over the top
hold on
[C,h] = contour(X,Y,Z);
clabel(C,h)
hold off

Sorting two column vectors into 3D matrix based on position

Using the imfindcircles function in MATLAB to track circles in two images. I start with approximately a grid of circles which deforms. I am trying to sort the two column vector from imfindcircles into matrices so that neighbouring circles are neighbouring elements in the matrices. The first image the circles conform to a grid and the following code works:
[centXsort,IX] = sortrows(centres1,1); %sort by x
centYsort =zeros(289,2); %preallocate
for i = 1:17:289
[sortedY,IY] = sortrows(centXsort(i:i+16,:),2); %sort by y within individual column
centYsort(i:i+16,:) = sortedY;
end
cent1mat = reshape(centYsort,17,17,2); %reshape into centre matrices
This doesn't work for the second image as some of the circles overlap in the x or y direction, but neighbouring circles never overlap. This means that in the second set of matrices the neighbouring circles aren't neighbouring elements after sorting.
Is there a way to approximate a scatter of points into a matrix?
This answer doesn't work in every single case, but it seems good enough for situations where the points don't vary too wildly.
My idea is to start at the grid corners and work our way along the outside diagonals of the matrix, trying to "grab" the nearest points that seem like they fit into the grid-points based any surrounding points we've already captured.
You will need to provide:
The number of rows (rows) and columns (cols) in the grid.
Your data points P arranged in a N x 2 array, rescaled to the unit square on [0,1] x [0,1]. (I assume the you can do this through visual inspection of the corner points of your original data.)
A weight parameter edge_weight to tell the algorithm how much the border points should be attracted to the grid border. Some tests show that 3-5 or so are good values.
The code, with a test case included:
%// input parameters
rows = 11;
cols = 11;
edge_weight = 4;
%// function for getting squared errors between the points list P and a specific point pt
getErr =#(P,pt) sqrt( sum( bsxfun(#minus,P,pt(:)').^2, 2 ) ); %'
output_grid = zeros(rows,cols,2); %// output grid matrix
check_grid = zeros(rows,cols); %// matrix flagging the gridpoints we have covered
[ROW,COL] = meshgrid(... %// coordinate points of an "ideal grid"
linspace(0,1,rows),...
linspace(0,1,cols));
%// create a test case
G = [ROW(:),COL(:)]; %// the actual grid-points
noise_factor = 0.35; %// noise radius allowed
rn = noise_factor/rows;
cn = noise_factor/cols;
row_noise = -rn + 2*rn*rand(numel(ROW),1);
col_noise = -cn + 2*cn*rand(numel(ROW),1);
P = G + [row_noise,col_noise]; %// add noise to get points
%// MAIN LOOP
d = 0;
while ~isempty(P) %// while points remain...
d = d+1; %// increase diagonal depth (d=1 are the outer corners)
for ii = max(d-rows+1,1):min(d,rows)%// for every row number i...
i = ii;
j = d-i+1; %// on the dth diagonal, we have d=i+j-1
for c = 1:4 %// repeat for all 4 corners
if i<rows & j<cols & ~check_grid(i,j) %// check for out-of-bounds/repetitions
check_grid(i,j) = true; %// flag gridpoint
current_gridpoint = [ROW(i,j),COL(i,j)];
%// get error between all remaining points and the next gridpoint's neighbours
if i>1
errI = getErr(P,output_grid(i-1,j,:));
else
errI = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i+1,j)
errI = errI + edge_weight*getErr(P,current_gridpoint);
end
if j>1
errJ = getErr(P,output_grid(i,j-1,:));
else
errJ = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i,j+1)
errJ = errJ + edge_weight*getErr(P,current_gridpoint);
end
err = errI.^2 + errJ.^2;
%// find the point with minimal error, add it to the grid,
%// and delete it from the points list
[~,idx] = min(err);
output_grid(i,j,:) = permute( P(idx,:), [1 3 2] );
P(idx,:) = [];
end
%// rotate the grid 90 degrees and repeat for next corner
output_grid = cat(3, rot90(output_grid(:,:,1)), rot90(output_grid(:,:,2)));
check_grid = rot90(check_grid);
ROW = rot90(ROW);
COL = rot90(COL);
end
end
end
Code for plotting the resulting points with edges:
%// plotting code
figure(1); clf; hold on;
axis([-0.1 1.1 -0.1 1.1])
for i = 1:size(output_grid,1)
for j = 1:size(output_grid,2)
scatter(output_grid(i,j,1),output_grid(i,j,2),'b')
if i < size(output_grid,1)
plot( [output_grid(i,j,1),output_grid(i+1,j,1)],...
[output_grid(i,j,2),output_grid(i+1,j,2)],...
'r');
end
if j < size(output_grid,2)
plot( [output_grid(i,j,1),output_grid(i,j+1,1)],...
[output_grid(i,j,2),output_grid(i,j+1,2)],...
'r');
end
end
end
I've developed a solution, which works for my case but might not be as robust as required for some. It requires a known number of dots in a 'square' grid and a rough idea of the spacing between the dots. I find the 'AlphaShape' of the dots and all the points that lie along the edge. The edge vector is shifted to start at the minimum and then wrapped around a matrix with the corresponding points are discarded from the list of vertices. Probably not the best idea for large point clouds but good enough for me.
R = 50; % search radius
xy = centres2;
x = centres2(:,1);
y = centres2(:,2);
for i = 1:8
T = delaunay(xy); % delaunay
[~,r] = circumcenter(triangulation(T,x,y)); % circumcenters
T = T(r < R,:); % points within radius
B = freeBoundary(triangulation(T,x,y)); % find edge vertices
A = B(:,1);
EdgeList = [x(A) y(A) x(A)+y(A)]; % find point closest to origin and rotate vector
[~,I] = min(EdgeList);
EdgeList = circshift(EdgeList,-I(3)+1);
n = sqrt(length(xy)); % define zeros matrix
matX = zeros(n); % wrap x vector around zeros matrix
matX(1,1:n) = EdgeList(1:n,1);
matX(2:n-1,n) = EdgeList(n+1:(2*n)-2,1);
matX(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,1);
matX(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,1);
matY = zeros(n); % wrap y vector around zeros matrix
matY(1,1:n) = EdgeList(1:n,2);
matY(2:n-1,n) = EdgeList(n+1:(2*n)-2,2);
matY(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,2);
matY(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,2);
centreMatX(i:n+i-1,i:n+i-1) = matX; % paste into main matrix
centreMatY(i:n+i-1,i:n+i-1) = matY;
xy(B(:,1),:) = 0; % discard values
xy = xy(all(xy,2),:);
x = xy(:,1);
y = xy(:,2);
end
centreMatX(centreMatX == 0) = x;
centreMatY(centreMatY == 0) = y;