You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
11 KiB

3 years ago
%% Tutorial and tests of TriangleRayIntersection function
% *By Jarek Tuszynski* (jaroslaw.w.tuszynski@leidos.com)
%
% Ray/triangle intersection using the algorithm proposed by M�ller and
% Trumbore (1997), implemented as highly vectorized MATLAB code.
%
% *Note* :
% The algorithm is able to solve several types of problems:
%
% * many faces / single ray intersection
% * one face / many rays intersection
% * one face / one ray intersection
% * many faces / many rays intersection
%
% In order to allow that to happen all input arrays are expected in Nx3
% format, where N is number of vertices or rays. In most cases number of
% vertices is different than number of rays, so one of the inputs will
% have to be cloned to have the right size. Use "repmat(A,size(B,1),1)" function.
%
% *Input* (all arrays in in Nx3 format, where N is number of vertices or rays):
%
% # orig : ray's origin
% # dir : ray's direction
% # vert0, vert1, vert2: vertices of the triangle mesh
% # aditional named parameters include
%
% * planeType - 'one sided' or 'two sided' (default) - how to treat
% triangles. In 'one sided' version only intersections in single
% direction are counted and intersections with back facing
% tringles are ignored
% * lineType - 'ray' (default), 'line' or 'segment' - how to treat rays:
% (1) 'line' means infinite (on both sides) line;
% (2) 'ray' means infinite (on one side) ray comming out of origin;
% (3) 'segment' means line segment bounded on both sides
% * border - controls border handling:
% (1) 'normal'(default) border - triangle is exactly as defined.
% Intersections with border points can be easily lost due to
% rounding errors.
% (2) 'inclusive' border - triangle is marginally larger.
% Intersections with border points are always captured but can
% lead to double counting when working with surfaces.
% (3) 'exclusive' border - triangle is marginally smaller.
% Intersections with border points are not captured and can
% lead to under-counting when working with surfaces.
% * epsilon - (default = 1e-5) controls border size
% * fullReturn - (default = false) controls returned variables t, u, v,
% and xcoor. By default in order to save time, not all t, u & v are
% calculated, only t, u & v for intersections can be expected.
% fullReturn set to true will force the calculation of them all.
%
%
% *Output:*
%
% * Intersect - boolean array of length N
% * t - distance from the ray origin to the intersection point in |dir|
% * u,v - barycentric coordinates of the intersection point units
% * xcoor - carthesian coordinates of the intersection point
%
%
%% Algorithm
% Function solves:
%
% $$\left[\begin{array}{ccc} -d_{x} & v1_{x}-v0_{x} & v2_{x}-v0_{x} \\ -d_{y} & v1_{y}-v0_{y} & v2_{y}-v0_{y} \\ -d_{z} & v1_{z}-v0_{z} & v2_{z}-v0_{z} \end{array}\right]\*\left[\begin{array}{c} t \\ u \\ v \end{array} \right]=\left[\begin{array}{c} o_{x}-v0_{x} \\ o_{y}-v0_{y} \\ o_{z}-v0_{z} \end{array}\right]$$
%
% for $\left[\begin{array}{c} t \\ u \\ v \end{array} \right]$.
%
% Variables _u_ , _v_ are barycentric coordinates and _t/|d|_ is the distance
% from the intersection point to the ray origin.
% Ray and triangle intersect if _u>=0, v>=0_ and _u+v<=1_ .
%
%% References
% Based on
%
% * "Fast, minimum storage ray-triangle intersection". Tomas M�ller and
% Ben Trumbore. Journal of Graphics Tools, 2(1):21--28, 1997.
% http://www.graphics.cornell.edu/pubs/1997/MT97.pdf
% * http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/raytri/
% * http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/raytri/raytri.c
%
%% Licence
% *The function is distributed under BSD License*
format compact; % viewing preference
clear variables; close all;
type('license.txt')
%% Create small surface and perform intersection with a ray (many faces / single ray type problem)
n=20;
[x,y] = meshgrid(1:n,1:n); % create 2D mesh of points
faces = delaunay(x,y); % triangulate it using Delaunay algorithm
z = peaks(n); % sample function defined on a grid of the same dimenision
vertices = [x(:) y(:) z(:)]; % vertices stored as Nx3 matrix
orig = [0.25*n 0 2]; % ray's origin
dir = [0.5 *n n 0]; % ray's direction
vert1 = vertices(faces(:,1),:);
vert2 = vertices(faces(:,2),:);
vert3 = vertices(faces(:,3),:);
tic;
intersect = TriangleRayIntersection(orig, dir, vert1, vert2, vert3);
fprintf('Number of: faces=%i, points=%i, intresections=%i; time=%f sec\n', ...
size(faces,1), size(vertices,1), sum(intersect), toc);
%%
% *Display the results: Surface in blue, line in light read and intersected
% triangles in dark red*
figure(1); clf;
trisurf(faces,x,y,z, intersect*1.0,'FaceAlpha', 0.9)
hold on;
line('XData',orig(1)+[0 dir(1)],'YData',orig(2)+[0 dir(2)],'ZData',...
orig(3)+[0 dir(3)],'Color','r','LineWidth',3)
set(gca, 'CameraPosition', [106.2478 -35.9079 136.4875])
%set(gco,'EdgeColor','none');
%% Create the same surface with much more elements and perform intersection with a ray
% *number of intersections should remain the same*
n=500;
[x,y] = meshgrid(1:n,1:n); % create 2D mesh of points
faces = delaunay(x,y); % triangulate it using Delaunay algorithm
z = peaks(n); % sample function defined on a grid of the same dimenision
vertices = [x(:) y(:) z(:)]; % vertices stored as Nx3 matrix
orig = [0.25*n 0 2]; % ray's origin
dir = [0.5 *n n 0]; % ray's direction
vert1 = vertices(faces(:,1),:);
vert2 = vertices(faces(:,2),:);
vert3 = vertices(faces(:,3),:);
tic;
intersect = TriangleRayIntersection(orig, dir, vert1, vert2, vert3);
fprintf('Number of: faces=%i, points=%i, intresections=%i; time=%f sec\n', ...
size(faces,1), size(vertices,1), sum(intersect), toc);
%% Triangulate a sphere and display it
n=50;
[x,y,z] = sphere(n);
DT = delaunayTriangulation(x(:), y(:), z(:));
[faces, vertices] = freeBoundary(DT);
figure(1); clf;
trisurf(faces, vertices(:,1),vertices(:,2),vertices(:,3),'FaceAlpha', 0.5)
axis equal
orig = [0 0 0]; % ray's origin
dir = [1 1 1]/2; % ray's direction
line('XData',orig(1)+[0 dir(1)],'YData',orig(2)+[0 dir(2)],'ZData',...
orig(3)+[0 dir(3)],'Color','r','LineWidth',3)
%% Add face normals
% Each triangle has 2 sides. Sides can be distingish from each other by
% calculating surface normal (http://en.wikipedia.org/wiki/Surface_normal)
% in case of our sphere all surface normals are pointing outwards
hold on
vert1 = vertices(faces(:,1),:);
vert2 = vertices(faces(:,2),:);
vert3 = vertices(faces(:,3),:);
faceCenter = (vert1+vert2+vert3)/3;
faceNormal = cross(vert2-vert1, vert3-vert1,2);
quiver3(faceCenter(:,1),faceCenter(:,2),faceCenter(:,3),...
faceNormal(:,1),faceNormal(:,2),faceNormal(:,3),3);
%% Intersect different types of lines with different surfaces
% this section varies 'lineType' and 'triangle' parameters
TestSet = {
'lineType', 'planeType', 'comment';
'line', 'two sided', 'infinite line intersects at 2 places';
'ray', 'two sided', 'ray comming from the center intersects at 1 place';
'segment', 'two sided', 'this segment is wholy within the sphere so no intersections';
'ray', 'one sided', ['same ray does not intersect with one sided '...
'sphere if the ray points oposite to face normal'];
};
fprintf('| %8s | %9s | %7s | %s\n%s\n',TestSet{1,1}, TestSet{1,2}, ...
'# found', TestSet{1,3}, repmat('-',1, 65))
for i = 2:size(TestSet,1)
intersect = TriangleRayIntersection(orig, dir, vert1, vert2, vert3,...
'lineType', TestSet{i,1}, 'planeType', TestSet{i,2});
fprintf('| %8s | %9s | %7d | %s\n',TestSet{i,1}, TestSet{i,2}, ...
sum(intersect), TestSet{i,3})
end
%% Example with many rays and many triangles (many faces / many rays type problem)
% *So far all examples were of a single ray and many triangles. However
% one can as well have one triangle and many rays, or many rays and many
% triangles. Example below calculates intersections between faces and rays
% goint through the center of each face. Since each intersection is in the
% same relative point t, u and v returned are very similar. Plot shows
% intersection points*
faceCenter = (vert1+vert2+vert3)/3;
Orig = repmat(orig,size(vert1,1),1); % Clone it until the same size as vert1
[intersect, t, u, v, xcoor] = TriangleRayIntersection(Orig, ...
2*(faceCenter-Orig), vert1, vert2, vert3);
fprintf('Number of: faces=%i, intresections=%i\n', size(faces,1), sum(intersect));
fprintf('mean t=%f+-%f\n', mean(t), std(t));
fprintf('mean u=%f+-%f\n', mean(u), std(u));
fprintf('mean v=%f+-%f\n', mean(v), std(v));
figure(1); clf;
plot3(xcoor(:,1), xcoor(:,2), xcoor(:,3), 'o')
%% Using option.border to customize border handling
% *Create simple tetrahedral and add a ray passing through one of the
% vertices*
[x,y] = pol2cart((0:2)'*2*pi/3,1);
vertices = [0 0 1; x y [0; 0; 0]];
faces = [1 2 3; 1 3 4; 1 4 2; 2 3 4];
figure(1); clf;
trisurf(faces, vertices(:,1),vertices(:,2),vertices(:,3),'FaceAlpha', 0.5);
view([3 1 1])
axis equal
vert1 = vertices(faces(:,1),:);
vert2 = vertices(faces(:,2),:);
vert3 = vertices(faces(:,3),:);
orig = [0 0 0.5]; % ray's origin
dir = [0 0 1]; % ray's destination
hold on;
line('XData',orig(1)+[0 dir(1)],'YData',orig(2)+[0 dir(2)],'ZData',...
orig(3)+[0 dir(3)],'Color','r','LineWidth',3)
%%
% *option.border controls border handling:*
%
% * border = 'normal' - border points are included, but can be easily
% lost due to rounding errors
% * border = 'inclusive' - border points are included, with margin
% of option.eps
% * border = 'exclusive' - border points are excluded, with margin
% of option.eps
intersect1 = TriangleRayIntersection(orig, dir, vert1, vert2, vert3, ...
'lineType' , 'ray', 'border', 'normal' );
intersect2 = TriangleRayIntersection(orig, dir, vert1, vert2, vert3, ...
'lineType' , 'ray', 'border', 'inclusive' );
intersect3 = TriangleRayIntersection(orig, dir, vert1, vert2, vert3, ...
'lineType' , 'ray', 'border', 'exclusive' );
fprintf('Number of intersections with border: normal=%i, inclusive=%i, exclusive=%i\n',...
sum(intersect1), sum(intersect2), sum(intersect3));
%% Test the intersection point location
% using the same figure
figure(1); clf;
trisurf(faces, vertices(:,1),vertices(:,2),vertices(:,3),'FaceAlpha', 0.5);
view([3 1 1])
axis equal
orig = [1 1 -1]*0.1; % ray's origin
dir = [0 0 1]; % ray's destination
hold on;
line('XData',orig(1)+[0 dir(1)],'YData',orig(2)+[0 dir(2)],'ZData',...
orig(3)+[0 dir(3)],'Color','r','LineWidth',3)
[intersect,~,~,~,xcoor] = TriangleRayIntersection(orig, dir, ...
vert1, vert2, vert3, 'lineType' , 'line');
scatter3(xcoor(intersect,1), xcoor(intersect,2), xcoor(intersect,3), 100, 'b', 'o', 'filled')
%% Test PointInsideVolume function
% PointInsideVolume function is an example of TriangleRayIntersection use
% A better function to do the same can be found at
% https://www.mathworks.com/matlabcentral/fileexchange/48041
load tetmesh;
TR = triangulation(tet,X);
[faces, vertices] = freeBoundary(TR);
n = 10000;
points = 80*rand(n,3) - repmat([40 40 0], n, 1);
in = PointInsideVolume(points, faces, vertices);
clf;
trisurf(faces,vertices(:,1),vertices(:,2),vertices(:,3), ...
'FaceColor','yellow','FaceAlpha', 0.2);
hold on
scatter3(points( in,1), points( in,2), points( in,3),30, 'r', 'fill');
scatter3(points(~in,1), points(~in,2), points(~in,3), 3, 'b', 'fill');
legend({'volume', 'points inside', 'points outside'}, 'Location', 'southoutside')