Link closest points accross altitudes in Matlab and form chains - matlab

I have a 3d matrix with scattered points (Nx4 matrix, x-y-z-data). My aim is to link the closest points together and register each chain in an Kx4 array (x, y, z, data), K being the chain length. The total number of chains depends on the points...
A particularity is that these lines only go upwards (z+), I don't want to link points on same z, or go down.
I have been trying different strategies so far, one being with another array shape (Mx4xNz - basically meaning the values were stacked per z's instead of being all on a 2d matrix):
[edited after some progress, using delaunay/nearestneighbor]
pick a point at level Zn
go to level Zn+1, look for the closest point in a range of coordinates x,y using delaunayTriangulation and nearestNeighbor
register the point into a vector
(I suspect there are other possibilities using nearestNeighbor with the Nx4 matrix, but i can't think how to 'direct' the search upwards and chain the successive points... )
I find myself with the following problem :
The finding of nearest point upwards seems to work well but in 1 direction only!!
Linking doesn't work:
Linking works:
During the loop I have the warning :
Warning: Duplicate data points have been detected and removed.
The Triangulation indices are defined with respect to the unique set of points in
delaunayTriangulation property X.
Lign=zeros(max_iter,4,s);
for i = 1:s;
pp_id=i;
for n=1:max_iter-1;
Wn=W(:,:,n); % W is the data 3d-matrix Mx4xNz
Wnn=W(:,:,n+1);
Point_n = Wn(pp_id,:);
xn= Point_n(1);
yn= Point_n(2);
zn= Point_n(3);
vn= Point_n(4);
if xn==0|yn==0|zn==0|vn==0;
break
end
% Look for nearest neighbour at next level
DT=delaunayTriangulation(Wnn(:,[1:2]));
[pp_id, d]=nearestNeighbor(DT,[Point_n(1),Point_n(2)]);
% limit range
if d>10
break
end
% extraction of values at new pp_id
Point_n=Wnn(pp_id,:);
% register point in line
Lign(n,:,i)=Point_n;
end
end
Anyone has an idea as to why this is happens?

Related

Append (x,y) coordinates to variable in Matlab loop for function call

I have scatter plot of several thousand points, which lie above a lower boundary defined by several line segments. My goal is to find the shortest distance from every point to the lower boundary (which is composed of linear and sloped line segments that connect) above which the pt lies, and sum this distance for all points for later post-processing.
I found a point-to-line function script online (https://www.mathworks.com/matlabcentral/fileexchange/64396-point-to-line-distance) which I have tested in isolation, and would now like to integrate into my script. The function take an array of points (class double) such as [0.1,0.7;0,0.5;...] and also takes two [x,y] points which lie on the line to which the shortest distance is to be calculated.
So far I have written a while loop which loops through all rows in a dataset already saved to the workspace (with the exception of zeroes which I would like to ignore). I then use a nested if loop to check if a given point is in the x range of the given lower boundary segment (I always want to calculate the shortest distance to the lower boundary segment above which a given pt lies), and lastly I try to append the given (x,y) coordinate of the point to a variable which will become one of the function inputs. The two points which define the lower boundary line segment are hard coded for each segment and do not change.
Here is a snippet of my code:
short_deviation = 0;
idx = 1;
while idx <= numel(my_data(:,5)) && not(my_data(idx,5) == 0)
...
if my_data(idx,5) < my_data(2,9) && my_data(idx,5) > my_data(1,9) % check that pt is in x range of lower segment
pt(:,idx) = my_data(idx,3:4); % CURRENT ERROR - Try to append given pt to list for function input
v1 = my_data(1,9:10); % two hard coded x,y pts which lie on lower boundary to which I want the distance
v2 = my_data(2,9:10);
distance_2D(idx) = point_to_line_dist(pt, v1, v2); % calling function
end
...
idx = idx + 1;
end
When I run the current code I get the following error message:
Unable to perform assignment because the size of the left side is 1-by-1 and the size of the right side is 1-by-2.
Error in My_script (line xxx)
pt(:,idx) = my_data(idx,3:4);
Now that I write out this code, I think another potential error is that I call the function distance_2D inside the if loop - I'm also not sure if the syntax for calling the function is correct (little experience here), but I haven't gotten to this point because of the previous error I mentioned.
The error indicates that pt is previously of the wrong size. For your code to run, it must have two rows, otherwise the data won't fit. You could use
pt=zeros(2,numel(my_data(:,5)))

Find minimum and maximum of a two variable function on fixed interval in Matlab, and plotting those points in the same graph with the function

I have this function below and I need to calculate the minimum and maximum of this function U, and also plotting the maximum and minimum value in 3D graph together with the function.
How can I write the code?
[x,y]=meshgrid(0:0.1:pi,0:0.1:2*pi);% x-theta,y-phi
a=90.7;b=36.2;c=12.9;
E=1.44;
U=-E.^2*(a.*sin(x).^2.*cos(y).^2+b.*sin(x).^2.*sin(y).^2+c.*cos(x).^2);
meshc(x,y,U)
xlabel('\theta')
ylabel('\Phi ')
zlabel('U')
I tired this way to find max but I don't know if i did it correct
max(U,[],1) %max row-wise
max(U,[],2) %max column-wise
and for the minimum it didn't work the same idea, also I didn't get the exact value of the maximum
As stated above, to simply find the maximum/minimum of your sampled function, use m = min(U(:)); M = max(U(:)). To be able to plot them, what you are missing are the coordinates that give you those values. For this, you will need the second output of the min/max functions, which gives you the index where the extreme happens.
A possible implementation (possibly not the best one) would be (might not work perfectly, I don't have matlab at hand):
[Ms,I] = max(U,[],1); %row-wise maximum and their indexes
[M,j] = max(Ms); %maximum among all rows and its index
Now i = I(j) is the location of the maximum. You can finally do plot3(x(i,j),y(i,j),U(i,j),'ro') to plot a big red circle in the maximums location, or whatever you like.
Note: I might have it backwards and it might be x(j,i), and so on. Just check. Of course you can do the same thing for min().
EDIT: I just remembered the ind2sub function , which solves all your problems. Following the syntax used above:
[M,ind] = max(U(:));
[i,j] = ind2sub(size(U),ind)
The rest holds the unchanged.
You can simply use something like
max(max(U))
this will find the maximum for your 2D matrix.
For the minimum you just have to replace max with min.

Testing whether a 3D point is inside a 3D polyhedron

Given a 3D polyhedron defined by its boundary represented by a triangulated mesh, how can I implement an algorithm that determines whether a given 3D point belongs to the interior of the polyhedron ?
There are several ways of implementing this function.
The simplest one is to create an infinite ray (or a very long segment) starting from the point and pointing towards an arbitrary direction, then counting the number of intersections between the ray and the triangles. If the number of intersections is odd, then the point is inside the polyhedron.
Inside(Polyhedron P, point q)
Segment S = [q, q+(0,0,1e30)]
count = 0
For each triangle T of P
If Intersect(S,T)
count = count + 1
End if
End for
return odd(count)
End
Now the function that computes whether there is an intersection between a segment and a triangle:
Intersect([q1,q2],(t1,t2,t3))
s1 = orient3d(q1,t1,t2,t3)
s2 = orient3d(q2,t1,t2,t3)
// Test whether the two extermities of the segment
// are on the same side of the supporting plane of
// the triangle
If(s1 == s2)
return false
End if
// Now we know that the segment 'straddles' the supporing
// plane. We need to test whether the three tetrahedra formed
// by the segment and the three edges of the triangle have
// the same orientation
s3 = orient3d(q1,q2,t1,t2)
s4 = orient3d(q1,q2,t2,t3)
s5 = orient3d(q1,q2,t3,t1)
return (s3 == s4 AND s4 == s5)
End
Finally, the orient3d function:
Orient3d(a,b,c,d)
// Computes the sign of the signed volume
// of the tetrahedron (a,b,c,d)
return Sign( dot(cross(b-a,c-a),d-a) )
End
Now there are two big gotchas:
what happens if floating point precision is not sufficient when
computing Orient3d ?
what happens when the chosen segment passes exactly through a
vertex or an edge of a triangle ?
For 1., one has to use arbitrary precision arithmetics [1]. There is a publicly available implementation of orient3d() by the author of reference [1] (Jonathan Shewchuk) in [2]. There is also an implementation in Geogram, my own programming library [3].
Now for 2., it is more tricky, the best way is to implement symbolic perturbations [4]. In a nutshell, the idea is to 'disambiguate' configurations where orient3d() returns zero by considering that the points are moving along trajectories parameterized by time, and taking the limit of the position when time tends to zero (another way of saying that: if position does not give the answer, take a look at the 'speed vector' at time t=0). The original reference [4] gives the symbolic perturbation for orient2d() for the "point in polygon" test (the 2D version of your problem).
Note: if you are lazy and you do not want to implement symbolic perturbation, you can pick a random direction everytime one of the orient3d() tests returns zero (then you have no guarantee that it will not search forever, but in practice it is very unlikely to occur). Anyway I'd recommend to use that only for prototyping, and implement symbolic perturbation in the end.
[1] https://people.eecs.berkeley.edu/~jrs/papers/robustr.pdf
[2] https://www.cs.cmu.edu/~quake/robust.html
[3] http://alice.loria.fr/software/geogram/doc/html/index.html
[4] http://dl.acm.org/citation.cfm?id=77639

Find elements of a matrix from another

I am work from a skeleton of an image. Wanting to fill some incomplete branches, I extracted the end points of the skeleton and implemented Dijkstra's algorithm to find the minimal paths that can be between the points (I get a matrix FR 500x500).
I applied a constraint of cost and orientation to limit the undesirable connections. My problem is that I still have unwanted links because some starting point belong to several paths with different costs. Then I extracted these starting points to keep only those whose cost is the lowest (matrix P 75x3 with column 1 and 2 which are the coordinates X and Y of the starting points and column 3 the cost).
I now try to code that I want to take all possible paths those who have a starting point and a cost corresponding to the matrix FP but I do not see how to do.
I would like to indicate in this code that each starting point has the "right" to take only the least expensive path
NumberPath = 0;
S=size(EP); %EP = matrix 90x2 coordinates x and y of skeleton's end points
NumP=S(1);
for i=1:NumP-1
for j=i+1:NumP
FP = ED(i,:);
LP = ED(j,:);
[cost,path] = dijkstra(PC,Weight); % PC = matrix of all possible paths
dimPath = size(path);
dx=LP(1)-FP(1);
dy=LP(2)-FP(2);
if dx>0 && dx/dy>0 % Constraints of orientation
NumberPath = NumberPath+1;
if (cost<55) % Constraints of Cost
for p=1:dimPath(2)
FR(PC(path(1,p),2),PC(path(1,p),1))=NumberPath;
% FR=Matrix 500x500 with all paths according to conditions
Cost(i,j)=cost; % Matrix of all cost < 55
P(NumberPath,:)=FP; % Starting Point of each path
L(NumberPath,:)=LP; % End Point of each path
end
end
end
end

Matlab Plot Smoothing having no effect

I'm currently having some trouble with the 'smooth' command, namely that it seems to have no effect on the generated plot. I have already used the following script to generate a plot
for h=1:4
linespec = {'rx', 'gx', 'bx', 'yx'};
hold on
for b=1:365
y=mean(mean(A(b,6*(h-1)+1:6*h)));
p(h)=plot(b,y,linespec{h});
end
hold off
end
Going row by row in data set A and taking the average of the values in the first six columns, then column 7 through 12, 13 through 18 and 19 through 14; generating four plots in total.
The next step was to smooth the resultant plot by averaging the values over a span of 9. So, I tweaked the script to the following;
for h=1:4
linespec = {'rx', 'gx', 'bx', 'yx'};
hold on
for b=1:365
y=mean(mean(A(b,6*(h-1)+1:6*h)));
w = smooth(y,9,'moving');
p(h)=plot(b,w,linespec{h});
end
hold off
end
Essentially just adding the w variable and replacing y with w in the plot command. Yet this has no effect whatsoever on my plot. Matlab doesn't throw up any errors either, so there doesn't seem to be a problem with the input size. Does anyone have an idea as to what the issue might be?
In either version of the loop, you appear to be plotting individual values of y against individual values of b. I presume, then, that y is a single value. You can't smooth a point, so the smooth operation is having no effect.
From the start, you don't need to make a loop to calculate the various means; mean can take a 2D matrix and return a vector. Calculate y in one go, then smooth that vector (should have length 365, I presume - depends on the size of input A). e.g.:
b = 1:365;
y=mean(A(:,6*(h-1)+1:6*h),2);
w = smooth(y,9,'moving');
plot(b,y,'rx');
hold on
plot(b,w,'gx');