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

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

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 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

Link closest points accross altitudes in Matlab and form chains

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?

Find the connected component in matlab

Given a BW image that contains some connected components.
Then, given a single pixel P in the image. How to find which component that contains the pixel P? It is guaranteed that the pixel P is always on the white area in one of the connected components.
Currently, I use CC = bwconncomp(BW) than I iterate each component using 'for' loop. In the each component, I iterate the index pixel. For each pixels, I check whether the value equal to the (index of) pixel P or not. If I find it, I record the number of connected component.
However, it seems it is not efficient for this simple task. Any suggestion for improvement? Thank you very much in advance.
MATLAB provides multiple functions that implement connected-component in different ways.
In your example, I would suggest bwlabel.
http://www.mathworks.com/help/images/ref/bwlabel.html
[L, num] = bwlabel(imgBW) This will perform a full-image connected-component labeling on a black-and-white image.
After calling this function, the label value that pixel P belongs to can be read off the result matrix L, as in label_to_find = L(row, col) index. Simple as that.
To extract a mask image for that label, use logical(L == label_to_find).
If you use different software packages such as OpenCV you will be able to get better performance (efficiency in terms of cutting unnecessary or redundant computation), but in MATLAB the emphasis is on convenience and prototyping speed.

Design optimization of material cost of water tank

One way to improve engineering designs is by formulating the equations describing the design in the form of a minimization or maximization problem. This approach is called design optimization. Examples of quantities to be minimized are energy consumption and construction materials. Items to be maximized are useful life and capacity such as the vehicle weight that can be supported by a bridge. In this project, we consider the problem of minimizing the material cost associated with building a water tank. The water tank consists of a cylindrical part of radius r and height h, and a hemispherical top. The tank is to be constructed to hold 500 meter cubed when filled. The surface area of the cylindrical part is 2*pi*rh, and its volume is pi*r^2. The surface area of the hemispherical top is given by 2*pi*r^2, and is volume is given by 2*pi*r^3/3. The cost to construct the cylindrical part of the tank is $300 per square meter of surface area;the hemispherical part costs $400 per square meter. Use the fminbnd function to compute the radius that results in the least cost. Compute the corresponding height h.
I got the right answer but it is very chaotic. I created bunch of function. I wonder if I can created one function?... let's name it ONEFUN
function R = findR(x)
h = (1500-2.*pi*x.^3)./(3.*pi.*x.^2);
R = 2.*pi.*x.*(h) + 2.*pi.*x.^2+pi.*x.^2;
function H = findH(x)
H = (1500-2.*pi*x.^3)./(3.*pi.*x.^2);
function [Cc, Chs, Tc] = Costs(r,h) % Cc - Cost of Cylinder, Chs - Cost of Hemishpere,
%Tc - Total Cost
Cc = ((2.*pi.*r.*h) + (pi.*r.^2)).*300;
Chs = (2.*pi.*r.^2).*400;
Tc = Cc+Chc;
I thought of using switch, response but I have no idea how to do it.
function Anwsers
response = input('Type "find r", "find h", "costHS", "costC", "total": ','s');
response = lower(response);
switch response
case 'find r'
Radius = fminsearch(#ONEFUN, [1]);
case 'find h'
Hight = findH(r)
case 'costHS'
case 'costC'
case 'total'
otherwise
disp('You have not entered a proper choice.')
end
I would appreciate and help
Doing it in one function is a bad idea. Lot's of simple function that do one thing each is good.
Most of the chaos from my point of view seems to be terse names, magic numbers, relying on operator precedence and duplication.
h = (1500- (2.*pi*x.^3)./(3.*pi.*x.^2)); for instance, I think ...
Why aren't you using the function of the same name? same code twice.
Where in Cthulhu's name do the numbers 1500, 300 and 400 come from?
Never been keen on single character function names myself, but that might be my lack of familiarity with expressing a problem mathematically.
This is a typical problem of minimizing a function with constraints. That is, you want to minimize the Cost(R,H), while keeping the Volume(R,H) fixed, and you have a simple (two-variable) equation for each of these.
For this you could use the matlab function fmincon.
The above is the most direct computational approach, but there are other ways to solve it using various degrees of incorporating the constraint into the solution analytically. You could, for example, do a full analytic solution, or solve the Volume equation for H, and then put this into to the Cost equation (ie, Cost(R,H)->Cost(R)) and then just minimize over R, etc. The approach you used is within this partially analytic middle-ground, but it's a bit messier for it.