I'm using MATLAB for the first time, and I have little experience with programming.
I have three coordinate points connected together with line segments to create a sort of zig-zag path. If the line segment from the origin to the first point was extended past the first point, I need to find the angle measure from the line extending from the first point to the line extending from the first point to the second point. This needs to be done for the second to the third point as well. I've read the solutions of similar questions, but I wasn't able to interpret and modify them for my situation.
Let's say your coordinates are:
coord = [1 2; 2 4; 1.5 1; 4 2]
coord =
1.0000 2.0000
2.0000 4.0000
1.5000 1.0000
4.0000 2.0000
This will give the following zig-zag pattern:
To find the angles of each line segment, you can do the following:
coord_diff = diff(coord) %// Find the difference between each
%// coordinate (i.e. the line between the points)
%// Make use of complex numbers. A vector is
%// given by x + i*y, where i is the imaginary unit
vector = coord_diff(:,1) + 1i * coord_diff(:,2);
line_angles = angle(vector) * 180/pi; %// Line angles given in degrees
diff_line_angle = diff(line_angles) %// The difference in angle between each line segment
This gives the following angles, which upon inspection of the graph seems reasonable.
line_angles =
63.4349
-99.4623
21.8014
diff_line_angle =
-162.8973
121.2637
Update after comments
coord = [0 0; 3 4; -1 7; 3 10]
coord =
0 0
3 4
-1 7
3 10
coord_diff = diff(coord) %// Find the difference between each
%// coordinate (i.e. the line between the points)
coord_diff =
3 4
-4 3
4 3
%// The angles of these lines are approximately 36.86 and 53.13 degrees
%// Make use of complex numbers. A vector is
%// given by x + i*y, where i is the imaginary unit
vector = coord_diff(:,1) + 1i * coord_diff(:,2);
line_angles = angle(vector) * 180/pi; %// Line angles given in degrees
line_angles =
53.1301
143.1301
36.8699
I'm not sure how you want to treat different signs etc., but something like this should work:
[90-line_angles(1), arrayfun(#(n) line_angles(n+1)-line_angles(n), ...
1:numel(line_angles)-1)].'
ans =
36.8699
90.0000
-106.2602
This is simpler, but harder to adapt in case you need to change signs or something similar:
[90-line_angles(1); diff(line_angles)]
Related
Given scatter data, or a matrix, I would like to generate a nice plot such as the one shown below, with all 3 histograms and a colored matrix. I'm specifically interested in the diagonal histogram, which ideally, would correspond to the diagonals of a matrix:
Source figure: www.med.upenn.edu/mulab/jpst.html
The existing command scatterhist is not that powerful to generate this type of graph. Any ideas?
Thanks!
EDIT:
Following #Cris Luengo's hints, I came up with the following code which does some first work at the inclined histogram: WORK IN PROGRESS (HELP WELCOME)!!
b = [0 1 2 3 4 5 6 7 8 9 10];
h = [0.33477 0.40166 0.20134 0.053451 0.008112 0.000643 2.7e-05 0 0 0 0];
wid = 0.25; bb = sort([b-wid b-wid b+wid b+wid]);
kk = [zeros(numel(h),1) h(:) h(:) zeros(numel(h),1)];
kk = reshape(kk',[1,numel(kk)]);
pp=patch(bb,kk,'b');axis([-.5 5 0 .5])
set(gca,'CameraUpVector',[-1,.08,0]);axis square
EDIT 2: Using rotation
phi = pi/4;
R = [cos(phi),-sin(phi);sin(phi),cos(phi)];
rr = [bb' kk'] * R;
bb = rr(:,1); kk = rr(:,2);
patch(bb,kk,'b'); axis([-.5 3 -4 .5])
Here is a recipe to plot the diagonal histogram, if you can do that I’m sure you can figure out the rest too.
Compute the histogram, the bin counts are h, the bin centers are b.
Build a coordinate matrix, attaching the coordinates of a point on the x-axis at the left and right ends of the histogram:
coords = [b(:),h(:)];
coords = [coord;b(end),0;b(1),0];
Using patch you can now plot the histogram as follows:
patch(coords(1,:),coords(2,:));
To plot a rotated histogram you can simply multiply the coords matrix with a rotation matrix, before using patch:
phi = pi/4;
R = [cos(phi),-sin(phi);sin(phi),cos(phi)];
coords = R * coords;
You might need to shift the plot to place it at the right location w.r.t. the other elements.
I recommend that you place all these graphic elements in the same axes object; you can set the axes’ visibility to 'off' so that it works only as a canvas for the other elements.
It will be a bit of work to get everything placed as in the plot you show, but none of it is difficult. Use the low-level image, line,patch and text to place those types of elements, don’t try to use the higher-level plotting functions such as plot, they don’t provide any benefits over the low-level ones in this case.
I have a plain with known four co-ordinates and a line with two known co-ordinates as shown in figure.
The four co-ordinates of plane are
A = (-5 -5 -8)
B = ( 15 15 -8)
C = ( 15 15 12)
D = ( -5 -5 12)
The co-ordinates of line are
M = (1.3978,40,6.1149)
N = 4.3943, 4.8078,0.3551)
In this case line and plain intersects,then how can I find point of intersection of line and plane in 3D space by using MATLAB?
or How can I check both are intersecting or not?
I have tried to find solution by following video tutorial to find equation of plane from three points and tutorial for finding point where line intersects a plain
But in my case, equation of plane is zero. So I am confused. Can anyone help me?
Thanks in advance,
Manu
I would use simple linear algebra to find the intersection point.
Let n be normal to the plain (you can calculate it as a vector product of say N = cross(AB, AD), then unit n = N / |N| where |N| = sqrt(dot(N, N)) is length of vector N.
You can use the following function from matlabcentral which covers all the corner cases as well (such as when the line is parallel to the plane) and describes them in the comments.
Example from comment:
A =[ -6.8756 39.9090 10.0000],B =[ -6.0096 40.4090 10.0000],C =[ -6.0096 40.4090 11.0000],D=[ -6.8756 39.9090 11.0000];
P0 =[ 1.3978 40.0000 6.1149],P1 =[ 4.3943 -4.8078 0.3551];
I don't know where you made a mistake, but I am pretty sure there is an intersection point which is outside your segment. So you should have got check=3. Here is the output of step by step operation:
>> AB = B-A
AB = 0.8660 0.5000 0
>> AD = D-A
AD = 0 0 1
>> n = cross(AB,AD)/sqrt(dot(cross(AB,AD),cross(AB,AD)))
n = 0.5000 -0.8660 0
>> [I,check]=plane_line_intersect(n,A,P0,P1)
I = 1.0961 44.5116 6.6948
check = 3
It produces the same results with any other point (B, C or D) passed in. check=3 means there is an intersection point I, which is outside of the P01 segment.
As a verification step, notice that normal n has Nz = 0 which means that it's perpendicular to the Z axis. The only way a line wouldn't intersect with it is if it would be parallel to Z axis (and therefore vector P01 would be parallel to Z and have zero Z component).
Your P01 is not aligned with Z:
>> P01 = P1 - P0
P01 = 2.9965 -44.8078 -5.7598
I am to fit planes through various points in an image, but I am having issues with forcing the line through a particular point in the image. This happens particularly when the line is 90 degrees.
My code is as follows:
I = [3 3 3 3 3 2 2
3 3 3 3 2 2 2
3 3 3 3 2 2 2
3 3 1 2 2 2 2
1 1 1 2 2 2 2
1 1 1 1 1 2 2
1 1 1 1 1 1 1];
% force the line through point p
p = [3,3];
% points to fit plane through
edgeA = [3,3.5; 3,4; 2.5,4; 2,4; 1.5,4];
edgeB = [3.5,3; 4,3; 4.5,3; 5,3];
% fit a plane through p and edgeA
xws = [p(2), edgeA(:,2)']';
yws = [p(1), edgeA(:,1)']';
Cws = [xws ones(size(xws))];
dws = yws;
Aeqws = [p(2) 1];
beqws = [p(1)];
planefitA = lsqlin(Cws ,dws,[],[],Aeqws, beqws);
% fit a plane through p and edgeB
xwn = [p(2), edgeB(:,2)']';
ywn = [p(1), edgeB(:,1)']';
Cwn = [xwn ones(size(xwn))];
dwn = ywn;
Aeqwn = [p(2) 1];
beqwn = [p(1)];
planefitB = lsqlin(Cwn ,dwn,[],[],Aeqwn, beqwn);
%%%%% plot the fitted planes:
xAxis = linspace(0, size(I, 2), 12);
%obtain linear curve
fA = planefitA(1)*xAxis + planefitA(2);
fB = planefitB(1)*xAxis + planefitB(2);
%plot the fitted curve
RI = imref2d(size(I),[0 size(I, 2)],[0 size(I, 1)]);
figure, imshow(I, RI, [], 'InitialMagnification','fit')
grid on;
hold on;
plot(xAxis,fA, 'Color', 'b', 'linewidth', 2);
plot(xAxis,fB, 'Color', 'r', 'linewidth', 2);
All the points in edgeB fall on a 90 degrees line. However, the function ends up fitting a wrong line through those points. I know this because using
planefitB = polyfit([p(2), edgeB(:,2)'], [p(1), edgeB(:,1)'], 1);
works for this particular line but the problem is that i have these process repeated so many times at different locations in my image, hence i do not know how to suggest polyfit when the line would be 90 degrees.
Please, any ideas/suggestions on how i could make this work? Many thanks.
This amounts to the least squares solution of only the angle of the line. The offset is fixed by the fact that it has to go through (3,3). The easiest way to express this is by offsetting your data points by the known crossing. That is, subtract (3,3) from your data points, and fit the best m for y=mx, the b being fixed to 0.
For the non-vertical case, you can use a classic least-squares formulation, but don't augment the constant 1 into the Vandermonde matrix:
slope = (edgeA(:,2) - p(2)) \ (edgeA(:,1) - p(1));
This gives exactly the same answer as your constrained lsq solution.
Now for the vertical line: A non-vertical line can be expressed in the standard functional form of y=mx, where the least squares formulation implicitly assumes an independent and a dependent variable. A vertical line doesn't follow that, so the only general choice is a "Total Least Squares" formulation, where errors in both variables are considered, rather than just the residuals in the dependent (y) variable.
The simplest way to write this is to choose a and b to minimize ax - by, in the least squares sense. [x_k -y_k]*[a b].' should be as close to a zero vector as possible. This is the vector closest to the null space of the [x -y] matrix, which can be computed with the svd. Swapping columns and fudging signs lets us just use svd directly:
[u s v] = svd(bsxfun(#minus, edgeA, p));
The last column of v is the closest to the null space, so mapping back to your x/y definitions, (edgeA-p)*v(:,2) is the line, so the y multiplier is in the top position, and the x in the lower, with a sign flip. To convert to y=mx form, just divide:
slope = -v(2,2)/v(1,2);
Note that this answer will be quite a bit different than the normal least squares answer, since you are treating the residuals differently. Also, the final step of computing "slope" won't work in the vertical case for the reasons we've already discussed (it produces Inf), so you are probably better off leaving the line as a normalized 2-vector, which won't have any corner cases.
I'm trying to do an algorithm in Matlab to try to calculate a received power in dBm of a logarithmic model of a wireless telecommunication system..
My algorithm calculate the received power for a number of distances in km that the user specified in the input and stores it in a vector
vector_distances = { 1, 5, 10, 50, 75 }
vector_Prx = { 131.5266 145.5060 151.5266 165.5060 169.0278 }
The thing is that I almost have everything that I need, but for graphics purposes I need to plot a graph in where on the x axys I have my vector of receiver power but on the y axys I want to show the same received power but with the most complete logarithmic model (the one that have also the noise - with Log-normal distribution on the formula - but for this thing in particular for every distance in my vector I need to choose 50 numbers with 0.5 distance between them (like a matrix) and then for every new point in the same distance calculate the logarithmic model to later plot in the same graph the two functions, one with the model with no noise (a straight line) and one with the noise.. like this picture
!http://imgur.com/gLSrKor
My question is, is there a way to choose 50 numbers with 0.5 distance between them for an existing number?
I know for example, if you have a vector
EDU>> m = zeros(1,5)
m =
0 0 0 0 0
EDU>> v = 5 %this is the starter distance%
v =
5
EDU>> m(1) = 5
m =
5 0 0 0 0
% I want to create a vector with 5 numbers with 0.5 distance between them %
EDU>> for i=2:5
m(i) = m(i-1) + 0.5
end
EDU>> m
m =
5.0000 5.5000 6.0000 6.5000 7.0000
But I have two problems, the firs one is, could this be more simplex? I am new on Matlab..and the other one, could I create a vector like this (with the initial number in the center)
EDU>> m
m =
4.0000 4.5000 **5.0000** 5.5000 6.0000
Sorry for my english, and thank you so much for helping me
In MATLAB, if you want to create a vector from a number n to a number m, you use the format
A = 5:10;
% A = [5,6,7,8,9,10]
You can also specify the step of the vector by including a third argument between the other two, like so:
A = 5:0.5:10;
% A = [5,5.5,6,6.5,7,7.5,8,8.5,9,9.5,10]
You can also use this to count backwards:
A = 10:-1:5
% A = [10,9,8,7,6,5]
Let a circle of known radius be plotted in MATLAB.
Assume a pair of random points whose location has to be determined in terms of coordinates (x1,y1) (x2,y2)..(xn,yn). Pairs should be close to each other. For example T1 and R1 should be near.
As shown in figure, there are four random pairs (T1,R1)..(T4,R4).
There coordinates need to be determined wrt to center (0,0).
How can I generate this in MATLAB?
The simplest approach to pick a point from a uniform distribution over a circle with reduce R is using Gibbs sampling. Here is the code:
function [x y] = circular uniform (R)
while true
x = 2*R*rand() - R
y = 2*R*rand() - R
if (x*x + y*y) > R*R
return
end
end
The loop runs 4/π times on average.
(Complete edit after the question was edited).
To complete this task, I think that you need to combine the different approaches that have been mentioned before your edit:
To generate the centers T1,T2,T3,... in the green torus, use the polar coordinates. (Edit: this turned out to be wrong, rejection sampling must also be used here, otherwise, the distribution is not uniform!)
To generate the points R1,R2,R3,... in the circle around T1,T2,T3,... but still in the torus, use the rejection sampling.
With these ingredients, you should be able to do everything you need. Here is the code I wrote:
d=859.23;
D=1432.05;
R=100;
N=400;
% Generate the angle
theta = 2*pi*rand(N,1);
% Generate the radius
r = d + (D-d)*rand(N,1);
% Get the centers of the circles
Tx = r.*cos(theta);
Ty = r.*sin(theta);
% Generate the R points
Rx=zeros(N,1);
Ry=zeros(N,1);
for i=1:N
while true
% Try
alpha = 2*pi*rand();
rr = R*rand();
Rx(i) = Tx(i) + rr*cos(alpha);
Ry(i) = Ty(i) + rr*sin(alpha);
% Check if in the correct zone
if ( (Rx(i)*Rx(i) + Ry(i)*Ry(i) > d*d) && (Rx(i)*Rx(i) + Ry(i)*Ry(i) < D*D) )
break
end
end
end
% Display
figure(1);
clf;
angle=linspace(0,2*pi,1000);
plot( d*cos(angle), d*sin(angle),'-b');
hold on;
plot( D*cos(angle), D*sin(angle),'-b');
for i=1:N
plot(Tx(i),Ty(i),'gs');
plot(Rx(i),Ry(i),'rx');
plot([Tx(i) Rx(i)],[Ty(i) Ry(i)],'-k');
end
hold off;
let R be radious of (0;0) centered circle.
(x,y) : x^2+y^2<=R^2 (LE) to be inside the circle
x = rand()*2*R - R;
y should be in interval (-sqrt(R^2 - x^2);+sqrt(R^2 - x^2))
so, let it be
y = rand()*sqrt(R^2 - x^2)*2-sqrt(R^2 - x^2);
Hope, that's right, i have no matlab to test.
Hope, you'll manage to find close pairs your self.
Ok, i'll spend a bit more time for a hint.
To find a random number k in interval [a,b] use
k = rand()*(b-a)+a
Now it should really help if i still remember the matlab syntaxis. Good luck.
Here is a low quality solution that is very easy to use with uniformly distributed points. Assuming the number of points is small efficiency should not be a concern, if you want better quality you can use something more powerfull than nearest neighbor:
While you have less than n points: Generate a random point
If it is in the circle, store it else go to step 1
While there are unpaired points: check which point is nearest to the first unpaired point, make them a pair
As a result most pairs should be good, but some can be really really bad. I would recommend you to try it and perhaps add a step 4 with k-opt or some other local search if required. And if you really have little points (e.g. less than 20) you can of course just calculate all distances and find the optimum matching.
If you don't really care about the uniform distribution, here is an even easier solution:
While you have less than n points: Generate a random point
If it is in the circle, store it else go to step 1
For each of these points, generate a point near it
If it is in the circle, store it else go to step 3
Generate the random points as #PheuVerg suggested (with a slight vectorized tweak)
n = 8; %must be even!
x = rand(n, 1)*2*R - R;
y = rand(n, 1).*sqrt(R^2 - x.^2).*2-sqrt(R^2 - x.^2);
Then use kmeans clustering to get n/2 centers
[~ c] = kmeans([x y], n/2);
now you have to loop through each center and find it's distance to each point
dists = zeros(n, n/2);
for cc = 1:n/2
for pp = 1:n
dists(pp, cc) = sqrt((c(cc,1) - x(pp))^2 + (c(cc,2) - y(pp))^2);
end
end
now you must find the smallest 2 values for each columns of dists
[sorted, idx] = sort(dists);
so now the top two rows of each column are the two nearest points. But there could be clashes! i.e. points that are nearest to two different centers. So for repeated values you have to loop through and choose swap for the point that will give you the smallest extra distance.
Example data:
x =
0.7894
-0.7176
-0.5814
0.0708
0.5198
-0.2299
0.2245
-0.8941
y =
-0.0800
-0.3339
0.0012
0.9765
-0.4135
0.5733
-0.1867
0.2094
sorted =
0.1870 0 0 0.1555
0.2895 0.5030 0.5030 0.2931
0.3145 1.1733 0.6715 0.2989
1.0905 1.1733 0.7574 0.7929
1.1161 1.2326 0.8854 0.9666
1.2335 1.2778 1.0300 1.2955
1.2814 1.4608 1.2106 1.3051
1.4715 1.5293 1.2393 1.5209
idx =
5 4 6 3
7 6 4 2
1 3 3 8
6 7 8 6
3 8 7 7
2 1 2 4
4 5 1 5
8 2 5 1
So now it's clear that 5 and 7 are pairs, and that 3 and 2 are pairs. But 4 and 6 are both repeated. (in this case it is clear that they are pairs too I guess!) but what I would suggest is to leave point 4 with center 2 and point 6 with center 3. Then we start at column 2 and see the next available point is 8 with a distance of 1.2326. This would leave point 1 paired with point 6 but then it's distance from the center is 1.2106. Had we paired point 6 with 8 and point 4 with 1 we would have got distances of 0.7574 and 1.2778 respectively which is actually less total distance. So finding 'close' pairs is easy but finding the set of pairs with the globally smallest minimum is hard! This solutions gets you something decent quite easily but fi you need the global best then I'm afraid you have quite a bit of work to do still :(
Finally let me add some visualisation. First lets (manually) create a vector that shows which points are paired:
I = [1 2 2 1 3 4 3 4];
Remember that that will depend on your data! Now you can plot is nicely like this:
gscatter(x, y, I)
Hope this gets you close and that you can eliminate the manual pairing of mine at the end by yourself. It shouldn't be too hard to get a crude solution.