How can I calculate the angles between a polygons vertices? - matlab

Due to my lack of MATLAB code knowledge, I was lost as to how I can calculate the angles between vertices of a random polygon.
Below I load and plot a polygon using the polyshape() function in which the x and y values are loaded from a txt file.
(loadpoly.m)
[filename,~] = uigetfile('*.txt', 'Load data from txt file');
if filename~=0
X_Y_Val = dlmread(filename, ' ');
end
X = X_Y_Val(:,1);
Y = X_Y_Val(:,2);
polygon = polyshape(X, Y);
plot(polygon)
(Randompolygon.txt)
0.29218 0.17609
0.56518 0.27635
0.69555 0.16324
0.83819 0.49486
0.62653 0.63882
0.27684 0.49743
My goal is to calculate the angles shown with the red marker and suggestions that can help me going in the right direction.

Related

Plotting contour for irregular xy data with holes

I am stuck with this problem for the last two days and haven't found a solution so far. I have a data in the following format:
x1, y1, val1
.. .. ..
.. .. ..
xn, yn, valn
The values val1, ..., valn are the field quantities I obtain after simulation on a geometry as below.
Only the grey region is the domain of interest whereas the one in blue/dark blue is not (including the inverted L shaped blue region in the interior). Thus the x and y coordinates of the data are scattered/irregular and with large gaps due to the hole in my original geometry. Is there a way to get a filled contour plot for this data? Trying the following in Matlab gives me triangulation with triangles outside the original polygon. Also, it fills the holes which is not what I want.
x = data(:,1);
y = data(:,2);
z = data(:,3);
%
dt = delaunayTriangulation(x,y) ;
tri = dt.ConnectivityList ;
xi = dt.Points(:,1) ;
yi = dt.Points(:,2) ;
F = scatteredInterpolant(x,y,z);
zI = F(xi,yi) ;
trisurf(tri,xi,yi,zI)
Another possibility was to import the data in ParaView and do filtering as Table-to-Points--> Delaunay Triangulation 2D. But this has the same problems as Matlab. The holes are not analytical to mask the unwanted interpolated regions with NaNs by using some mathematical expression.
Paraview seems to have the solution for this. Although I did not use finite elements to solve the pde, I could generate a finite element mesh inside GMsh for my geometry with holes. I then import both my CSV data file and the GMsh mesh file (in .vtk format) in ParaView. Resampling my field data with Dataset filter with the results of the Delaunay2D as the input gives me the contour only on the original geometry.

Matlab - Find Coordinates between a straight line and a perimeter

I segmented a mouse and get its image-properties using bwlabel. Thereby I have access to the position of the centroid and the orientation of the mouse. I also get the perimeter of the mouse using bwperim.
I want to find the two points of the straight line passing through the centroid and having the same direction than the orientation of the mouse cutting the perimeter.
I find the equation of the straight line using that code :
% E is a 2*2 matrix containing the coordinates of the centroid and the
% coordinates of the point which belong to the straight line and making
% the right angle given by the orientation
coeffs = polyfit(E(:,1),E(:,2),1);
% Create the equation of the straight line
x = 1:width;
yfit = coeffs(1)*x+coeffs(2);
% Make sure there are only int values.
yfit = uint16(yfit);
I convert my values to uint16 because i want to fill a new matrix that I will compare with the matrix containing the perimeter. Here is what I do then :
% Create a matrix of zeros and set to 1 all the pixels which belong to the
% straight line
k = 1;
temp = false;
m = false(size(iPerim));
while temp~=true
temp = false;
if yfit(k) > 0
m(yfit(k),k)=1;
temp = true;
end
k = k+1;
end
[t,p] = ind2sub(size(m), find(m==1));
minM = [min(p),min(t)];
% complete the straight line to don't have little holes
x = linspace(minM(1),D(1),width);
y = coeffs(1)*x+coeffs(2);
idx = sub2ind(size(m),round(y),round(x));
m(idx) = 1;
Then I compare m with iPerim which is the matrix containing my perimeter:
% Compare the matrix of the perimeter and the matrix of the straight line
% and find the two points in common. It is the points where the straight
% line cut the perimeter
p = m & iPerim;
% Extract thoses coordinates
[coordsY,coordsX] = ind2sub(size(p), find(p==1));
Well I am a new user of Matlab so I think this is not a elegant solution but there is the result:
Matrix m
Perimeter in which I plot yfit
As you can see the algorithm detects only one point and not the second one (the yellow spot)... I figure why but I can't find the solution. It is because the line straight is cutting the perimeter through a diagonal but there are not coordinates in common...
Somebody has a solution to my problem ? And of course I am taking any advises conerning my code :)
Thank you very much !
Edit: If there is a easier solution I take it obviously
When the coordinate of the point where the mouse-perimeter and the line cross are E(2,:), then the position of this point in the line is where the distance is minimal. E.g. like:
[xLine, yLine] = find(m); % x,y positions of the line
dX = abs(xline-E(2,1)) % x-distance to x-coordinate of direction-point
dY = abs(yLine-E(2,2)) % y-distance to y-coordinate of direction-point
distP = sqrt(dX.^2+dY.^2) % distance of line-points to directon-point
[~,indMin] = min(distP); % index of line-point which has the minimum distance
xPoint = xLine(indMin(1));
yPoint = yLine(indMin(1));
The abs and sqrtfunctions are not necessary here for finding the right point, only for the correct intermediate values...
From the Matlab Documentation about ind2sub:
For matrices, [I,J] = ind2sub(size(A),find(A>5)) returns the same values as [I,J] = find(A>5).

How would I plot the for loop from my code below?

I have 3D flow data of the velocity of a fluid through a tube. I know the diameter of the tube and have looked at the velocity field and found the centre of the field for an xy plane at both ends of the tube. So I essentially have a line through the centre axis of the tube. I want to NaN all data points that are outside of the diameter. For this I am using an equation that gives the distance to a point from a line in 3D which I found here mathworld.wolfram.com/Point-LineDistance3-Dimensional.html. I then created an if statement which states points smaller than diameter will be NaN.
I am new to matlab so I don't know how I would now plot this.
%%
diff_axis = end_axis-start_axis;
diff_axis_mag = (diff_axis(1)^2 + diff_axis(2)^2 + diff_axis(3)^2)^0.5;
[rw col pl] = size(X);
for j = 1:col
for i = 1:rw
for k = 1:pl
x_curr = X(i,j,k);
y_curr = Y(i,j,k);
z_curr= Z(i,j,k);
x0 = [x_curr y_curr z_curr]
t = - dot((start_axis-x0),(diff_axis))./(diff_axis_mag)^2;
d = sqrt(((start_axis(1) - x0(1)) + (end_axis(1) - start_end(1))*t)^2 + ((start_axis(2)-x0(2))+(end_axis(2)-start_end(2))*t)^2+((start_axis(3)-x0(3))+(end_axis(3)-start_end(3))*t)^2);
if (d > D)
x_curr=NaN
y_curr=NaN
z_curr=NaN
end
end
end
end
It were nice to have explanatory names for your X, Y, and Z. I am guessing they are flow components, and diff_axis are axis coordinates? It is a very cumbersome notation.
what you do in your loops is you take point values (X,Y,Z), copy them to temporary constants and then set them to NaN if they fall out. But the problem is that usually you do not plot point-by-point in MATLAB. So these temorary guys like x_curr will be lost.
Also, the most optimal way to do things in MATLAB is to avoid loops whenever possible.
What you can do is to create first a mask
%// remember to put a dot like in `.^` for entrywise array operations
diff_axis_mag = sqrt(diff_axis(1).^2 + diff_axis(2).^2 + diff_axis(3).^2);
%// are you sure you need to include the third axis?
%// then it is a ball, not a tube
%// create a binary mask
mask = diff_axis_mag < tube_radius
X(~mask) = NaN;
Y(~mask) = NaN;
Z(~mask) = NaN;
Then you can plot your data with quiver3 or
stream3

Plot random lines starting from specific point in a sphere in MatLab

I am trying to plot random lines, starting from a specific radius of a sphere, but I only want the upper hemisphere, as shown in the image
So far I am able to create random starting points(but for R=15), random intersections, random slopes, but I don't know how to connect all these to plot the lines.
My code is
%Create the random starting points, slopes, intersections
tracks=input('Give me the number of muon tracks: ');
theta=180.*rand(tracks,1);
rho=15*ones(tracks,1);
startPoint = [theta rho];
[X,Y]=pol2cart(theta*pi/180,rho);
intersection =-6371+(2*6371).*rand(tracks,1);
slope = tand(360.*rand(tracks,1));
I know that I need only two elements to draw a line, but I kind of confused right now...
Any idea on how to do it?
Because you don't want MATLAB to join up all of your lines when you plot them, you need to plot them separately, in a loop, e.g., something like
theta = 2 * pi * rand(tracks, 2); % 2 rows of random points on a circle, in radians
X = cos(theta); Y = sin(theta);
close all;
figure;
hold on;
for nPlot = 1:tracks
plot(X(nPlot, :), Y(nPlot, :), 'r-o');
end
Note that this code also generates X and Y differently to your original - pol2cart and the above method both expect values in radians, not degrees.

How to draw a straight across the centroid points of the barcode using best fit points Matlab

This is the processed image and I can't increase the bwareaopen() as it won't work for my other image.
Anyway I'm trying to find the shortest points in the centre points of the barcode, to get the straight line across the centre points in the barcode.
Example:
After doing a centroid command, the points in the barcode are near to each other. Therefore, I just wanted to get the shortest points(which is the barcode) and draw a straight line across.
All the points need not be join, best fit points will do.
Step 1
Step 2
Step 3
If you dont have the x,y elements Andrey uses, you can find them by segmenting the image and using a naive threshold value on the area to avoid including the number below the bar code.
I've hacked out a solution in MATLAB doing the following:
Loading the image and making it binary
Extracting all connected components using bwlabel().
Getting useful information about each of them via regionprops() [.centroid will be a good approximation to the middel point for the lines].
Thresholded out small regions (noise and numbers)
Extracted x,y coordinates
Used Andreys linear fit solution
Code:
set(0,'DefaultFigureWindowStyle','docked');
close all;clear all;clc;
Im = imread('29ekeap.jpg');
Im=rgb2gray(Im);
%%
%Make binary
temp = zeros(size(Im));
temp(Im > mean(Im(:)))=1;
Im = temp;
%Visualize
f1 = figure(1);
imagesc(Im);colormap(gray);
%Find connected components
LabelIm = bwlabel(Im);
RegionInfo = regionprops(LabelIm);
%Remove background region
RegionInfo(1) = [];
%Get average area of regions
AvgArea = mean([RegionInfo(1:end).Area]);
%Vector to keep track of likely "bar elements"
Bar = zeros(length(RegionInfo),1);
%Iterate over regions, plot centroids if area is big enough
for i=1:length(RegionInfo)
if RegionInfo(i).Area > AvgArea
hold on;
plot(RegionInfo(i).Centroid(1),RegionInfo(i).Centroid(2),'r*')
Bar(i) = 1;
end
end
%Extract x,y points for interpolation
X = [RegionInfo(Bar==1).Centroid];
X = reshape(X,2,length(X)/2);
x = X(1,:);
y = X(2,:);
%Plot line according to Andrey
p = polyfit(x,y,1);
xMin = min(x(:));
xMax = max(x(:));
xRange = xMin:0.01:xMax;
yRange = p(1).*xRange + p(2);
plot(xRange,yRange,'LineWidth',2,'Color',[0.9 0.2 0.2]);
The result is a pretty good fitted line. You should be able to extend it to the ends by using the 'p' polynomal and evaluate when you dont encounter any more '1's if needed.
Result:
If you already found the x,y of the centers, you should use polyfit function:
You will then find the polynomial coefficients of the best line. In order to draw a segment, you can take the minimal and maximal x
p = polyfit(x,y,1);
xMin = min(x(:));
xMax = max(x(:));
xRange = xMin:0.01:xMax;
yRange = p(1).*xRange + p(2);
plot(xRange,yRange);
If your ultimate goal is to generate a line perpendicular to the bars in the bar code and passing roughly through the centroids of the bars, then I have another option for you to consider...
A simple solution would be to perform a Hough transform to detect the primary orientation of lines in the bar code. Once you find the angle of the lines in the bar code, all you have to do is rotate that by 90 degrees to get the slope of a perpendicular line. The centroid of the entire bar code can then be used as an intercept for this line. Using the functions HOUGH and HOUGHPEAKS from the Image Processing Toolbox, here's the code starting with a cropped version of your image from step 1:
img = imread('bar_code.jpg'); %# Load the image
img = im2bw(img); %# Convert from RGB to BW
[H, theta, rho] = hough(img); %# Perform the Hough transform
peak = houghpeaks(H); %# Find the peak pt in the Hough transform
barAngle = theta(peak(2)); %# Find the angle of the bars
slope = -tan(pi*(barAngle + 90)/180); %# Compute the perpendicular line slope
[y, x] = find(img); %# Find the coordinates of all the white image points
xMean = mean(x); %# Find the x centroid of the bar code
yMean = mean(y); %# Find the y centroid of the bar code
xLine = 1:size(img,2); %# X points of perpendicular line
yLine = slope.*(xLine - xMean) + yMean; %# Y points of perpendicular line
imshow(img); %# Plot bar code image
hold on; %# Add to the plot
plot(xMean, yMean, 'r*'); %# Plot the bar code centroid
plot(xLine, yLine, 'r'); %# Plot the perpendicular line
And here's the resulting image: