|
a |
|
b/SurfaceIntersection.m |
|
|
1 |
function [intMatrix, intSurface] = SurfaceIntersection(surface1, surface2, varargin) |
|
|
2 |
%SURFACEINTERSECTION intersection of 2 surfaces |
|
|
3 |
% [intMatrix, intSurface] = SurfaceIntersection(surface1, surface2) |
|
|
4 |
% calculates the intersection of surfaces 1 and 2. Code can either return |
|
|
5 |
% just the matrix indicating which face of surface1 intersected with face |
|
|
6 |
% of surface2, which is calculated using Tomas Moller algorithm, or can |
|
|
7 |
% also return the actual line of intersection. In case when parts of the |
|
|
8 |
% surface 1 and 2 lay on the same plane the intersection is a 2D area |
|
|
9 |
% instead of 1D edge. In such a case the intersection area will be |
|
|
10 |
% triangulated and intSurface.edges will hold the edges of the |
|
|
11 |
% triangulation surface and intSurface.faces will hold the faces. |
|
|
12 |
% |
|
|
13 |
% INPUT: |
|
|
14 |
% * surface1 & surface2 - two surfaces defined as structs or classes. |
|
|
15 |
% Several inputs are possible: |
|
|
16 |
% - struct with "faces" and "vertices" fields |
|
|
17 |
% - 'triangulation' class (only the boundary surface will be used) |
|
|
18 |
% - 'delaunayTriangulation' class |
|
|
19 |
% |
|
|
20 |
% OUTPUT: |
|
|
21 |
% * intMatrix - sparse Matrix with n1 x n2 dimension where n1 and n2 are |
|
|
22 |
% number of faces in surfaces |
|
|
23 |
% * intSurface - a structure with following fields: |
|
|
24 |
% intSurface.vertices - N x 3 array of unique points |
|
|
25 |
% intSurface.edges - N x 2 array of edge vertex ID's |
|
|
26 |
% intSurface.faces - N x 3 array of face vertex ID's |
|
|
27 |
% |
|
|
28 |
% ALGORITHM: |
|
|
29 |
% Based on Triangle/triangle intersection test routine by Tomas Mller, 1997. |
|
|
30 |
% See article "A Fast Triangle-Triangle Intersection Test", |
|
|
31 |
% Journal of Graphics Tools, 2(2), 1997 |
|
|
32 |
% http://web.stanford.edu/class/cs277/resources/papers/Moller1997b.pdf |
|
|
33 |
% http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/opttritri.txt |
|
|
34 |
|
|
|
35 |
%% Get FACES and VERTICES inputs |
|
|
36 |
if isa(surface1, 'triangulation') |
|
|
37 |
[surface1.faces, surface1.vertices] = freeBoundary(surface1); |
|
|
38 |
elseif isa(surface1, 'delaunayTriangulation') |
|
|
39 |
S = surface1; |
|
|
40 |
surface1 = []; |
|
|
41 |
surface1.faces = S.ConnectivityList; |
|
|
42 |
surface1.vertices = S.Points; |
|
|
43 |
clear S |
|
|
44 |
end |
|
|
45 |
if isa(surface2, 'triangulation') |
|
|
46 |
[surface2.faces, surface1.vertices] = freeBoundary(surface2); |
|
|
47 |
elseif isa(surface2, 'delaunayTriangulation') |
|
|
48 |
S = surface2; |
|
|
49 |
surface2 = []; |
|
|
50 |
surface2.faces = S.ConnectivityList; |
|
|
51 |
surface2.vertices = S.Points; |
|
|
52 |
clear S |
|
|
53 |
end |
|
|
54 |
ok1 = isstruct(surface1) && isfield(surface1, 'vertices') && isfield(surface1, 'faces'); |
|
|
55 |
ok2 = isstruct(surface2) && isfield(surface2, 'vertices') && isfield(surface2, 'faces'); |
|
|
56 |
assert(ok1, 'Surface #1 must be a struct with "faces" and "vertices" fields' ); |
|
|
57 |
assert(ok2, 'Surface #2 must be a struct with "faces" and "vertices" fields' ); |
|
|
58 |
|
|
|
59 |
%% Flip dimentions if necessery |
|
|
60 |
if size(surface1.faces,1)==3 && size(surface1.faces,2)~=3 |
|
|
61 |
surface1.faces = surface1.faces'; |
|
|
62 |
end |
|
|
63 |
if size(surface1.vertices,1)==3 && size(surface1.vertices,2)~=3 |
|
|
64 |
surface1.vertices = surface1.vertices'; |
|
|
65 |
end |
|
|
66 |
if size(surface2.faces,1)==3 && size(surface2.faces,2)~=3 |
|
|
67 |
surface2.faces = surface2.faces'; |
|
|
68 |
end |
|
|
69 |
if size(surface2.vertices,1)==3 && size(surface2.vertices,2)~=3 |
|
|
70 |
surface2.vertices = surface2.vertices'; |
|
|
71 |
end |
|
|
72 |
|
|
|
73 |
%% Parse extra parameters |
|
|
74 |
getIntersection = (nargout>1); |
|
|
75 |
debug = true; |
|
|
76 |
PointRoundingTol = 1e6; |
|
|
77 |
algorithm = 'moller'; |
|
|
78 |
k=1; |
|
|
79 |
nVarargs = length(varargin); |
|
|
80 |
while (k<=nVarargs) |
|
|
81 |
assert(ischar(varargin{k}), 'Incorrect input parameters') |
|
|
82 |
switch lower(varargin{k}) |
|
|
83 |
case 'debug' |
|
|
84 |
debug = varargin{k+1}~=0; |
|
|
85 |
k = k+1; |
|
|
86 |
case 'algorithm' |
|
|
87 |
algorithm = lower(strtrim(varargin{k+1})); |
|
|
88 |
k = k+1; |
|
|
89 |
case 'pointroundingtol' |
|
|
90 |
PointRoundingTol = varargin{k+1}; |
|
|
91 |
k = k+1; |
|
|
92 |
end |
|
|
93 |
k = k+1; |
|
|
94 |
end |
|
|
95 |
|
|
|
96 |
%% Initialize variables |
|
|
97 |
epsilon = eps; |
|
|
98 |
nFace1 = size(surface1.faces,1); |
|
|
99 |
nFace2 = size(surface2.faces,1); |
|
|
100 |
nVert1 = size(surface1.vertices,1); |
|
|
101 |
nVert2 = size(surface2.vertices,1); |
|
|
102 |
|
|
|
103 |
%% create strip down versions of MATLAB cross and dot function |
|
|
104 |
cross_prod = @(a,b) [... |
|
|
105 |
a(:,2).*b(:,3)-a(:,3).*b(:,2), ... |
|
|
106 |
a(:,3).*b(:,1)-a(:,1).*b(:,3), ... |
|
|
107 |
a(:,1).*b(:,2)-a(:,2).*b(:,1)]; |
|
|
108 |
dot_prod = @(a,b) a(:,1).*b(:,1)+a(:,2).*b(:,2)+a(:,3).*b(:,3); |
|
|
109 |
normalize = @(V) bsxfun(@rdivide,V, sqrt(sum(V.^2,2))); |
|
|
110 |
|
|
|
111 |
%% Initialize output variables |
|
|
112 |
% intersect is a nFace1 x nFace2 matrix. Possible values: -2 (do not know), |
|
|
113 |
% -1 (coplanar with unknown overlap), 0 (no intersections), 1 (intersects). |
|
|
114 |
% Negative values are internal only. |
|
|
115 |
intMatrix = zeros([nFace1,nFace2], 'int8')-2; % -2 indicates that there was no succesful test yet |
|
|
116 |
intSurface.vertices = []; |
|
|
117 |
intSurface.faces = []; |
|
|
118 |
intSurface.edges = []; |
|
|
119 |
|
|
|
120 |
% ======================================================================= |
|
|
121 |
%% === Stage 1 ========================================================== |
|
|
122 |
% ======================================================================= |
|
|
123 |
% Each triangle is a subset of the plane it lies in, so for two triangles |
|
|
124 |
% to intersect they must overlap along the line of intersection of their |
|
|
125 |
% planes. Hence, a necessary condition for intersection is that each |
|
|
126 |
% triangle must intersect the plane of the other. |
|
|
127 |
% Mllers method begins by checking the mutual intersection of each |
|
|
128 |
% triangle with the plane of the other. To do so, it determines for each |
|
|
129 |
% triangle on which side of the other triangles supporting plane its |
|
|
130 |
% vertices lie. Now, if all vertices of one triangle lie on the same side |
|
|
131 |
% and no vertex is on the plane, the intersection is rejected. |
|
|
132 |
|
|
|
133 |
%% compute plane equations for each triangle of the surface #1 |
|
|
134 |
% plane equation #1: N1.X-d1=0 |
|
|
135 |
V1 = surface1.vertices(surface1.faces(:,1),:); |
|
|
136 |
V2 = surface1.vertices(surface1.faces(:,2),:); |
|
|
137 |
V3 = surface1.vertices(surface1.faces(:,3),:); |
|
|
138 |
N1 = cross_prod(V2-V1,V3-V1); % array size nFace1 x 3 |
|
|
139 |
N1 = normalize(N1); |
|
|
140 |
d1 = dot_prod(N1,V1); % array size nFace1 x 1 |
|
|
141 |
|
|
|
142 |
%% Distance from surface #2 vertices to planes of surface #1 |
|
|
143 |
% Calculate signed distance from all vertices of surface #2 to each plane |
|
|
144 |
% of of surface #1 |
|
|
145 |
du = zeros(nFace1,nVert2); |
|
|
146 |
for iVert2 = 1:nVert2 |
|
|
147 |
p = surface2.vertices(iVert2,:); |
|
|
148 |
du(:,iVert2) = N1(:,1)*p(1) + N1(:,2)*p(2) + N1(:,3)*p(3) - d1; |
|
|
149 |
end |
|
|
150 |
if debug |
|
|
151 |
assert(all(size(du)==[nFace1,nVert2]), 'Incorrect array dimensions: dv') |
|
|
152 |
end |
|
|
153 |
du(abs(du)<epsilon)=0; % robustness check |
|
|
154 |
% Distances from vertex 1, 2 & 3 of faces of surface #2 to planes of surface #1 |
|
|
155 |
du1 = du(:,surface2.faces(:,1)); |
|
|
156 |
du2 = du(:,surface2.faces(:,2)); |
|
|
157 |
du3 = du(:,surface2.faces(:,3)); |
|
|
158 |
if debug |
|
|
159 |
assert(all(size(du1)==size(intMatrix)), 'Incorrect array dimensions: du1') |
|
|
160 |
end |
|
|
161 |
clear du |
|
|
162 |
intMatrix(du1.*du2>0 & du1.*du3>0) = 0; % same sign on all of them & not equal 0 |
|
|
163 |
if(all(intMatrix==0)), return; end % no intersections |
|
|
164 |
intMatrix(du1==0 & du2==0 & du3==0) = -1; % coplanar with unknown overlap |
|
|
165 |
|
|
|
166 |
%% compute plane of triangle (U0,U1,U2) |
|
|
167 |
% plane equation 2: N2.X-d2=0 |
|
|
168 |
U1 = surface2.vertices(surface2.faces(:,1),:); |
|
|
169 |
U2 = surface2.vertices(surface2.faces(:,2),:); |
|
|
170 |
U3 = surface2.vertices(surface2.faces(:,3),:); |
|
|
171 |
N2 = cross_prod(U2-U1,U3-U1); % array size nFace1 x 3 |
|
|
172 |
N2 = normalize(N2); |
|
|
173 |
d2 = dot_prod(N2,U1); % array size nFace1 x 1 |
|
|
174 |
|
|
|
175 |
%% Distance from surface #1 vertices to planes of surface #2 |
|
|
176 |
% Calculate signed distance from all vertices of surface #1 to each plane |
|
|
177 |
% of of surface #2 |
|
|
178 |
dv = zeros(nFace2,nVert1); |
|
|
179 |
for iVert1 = 1:nVert1 |
|
|
180 |
p = surface1.vertices(iVert1,:); |
|
|
181 |
dv(:,iVert1) = N2(:,1)*p(1) + N2(:,2)*p(2) + N2(:,3)*p(3) - d2; |
|
|
182 |
end |
|
|
183 |
if debug |
|
|
184 |
assert(all(size(dv)==[nFace2,nVert1]), 'Incorrect array dimensions: dv') |
|
|
185 |
end |
|
|
186 |
dv(abs(dv)<epsilon)=0; % robustness check |
|
|
187 |
% Distances from vertex 1, 2 & 3 of faces of surface #1 to planes of surface #2 |
|
|
188 |
dv1 = dv(:,surface1.faces(:,1))'; |
|
|
189 |
dv2 = dv(:,surface1.faces(:,2))'; |
|
|
190 |
dv3 = dv(:,surface1.faces(:,3))'; |
|
|
191 |
if debug |
|
|
192 |
assert(all(size(dv1)==size(intMatrix)), 'Incorrect array dimensions: dv1') |
|
|
193 |
end |
|
|
194 |
clear dv |
|
|
195 |
intMatrix(dv1.*dv2>0 & dv1.*dv3>0) = 0; % same sign on all of them & not equal 0 |
|
|
196 |
if(all(intMatrix==0)), return; end % no intersections |
|
|
197 |
intMatrix(dv1==0 & dv2==0 & dv3==0) = -1; % coplanar with unknown overlap |
|
|
198 |
|
|
|
199 |
% ======================================================================= |
|
|
200 |
%% === Stage 2 ========================================================== |
|
|
201 |
% ======================================================================= |
|
|
202 |
|
|
|
203 |
%% Process remaining (non-coplanar) triangle pairs |
|
|
204 |
tMsk = (intMatrix==-2); |
|
|
205 |
n = nnz(tMsk); |
|
|
206 |
if n>0 |
|
|
207 |
[face1, face2] = find(tMsk); |
|
|
208 |
switch lower(algorithm) |
|
|
209 |
case 'moller' |
|
|
210 |
if size(dv1(tMsk),1)==1 |
|
|
211 |
dv = [dv1(tMsk)', dv2(tMsk)', dv3(tMsk)']; |
|
|
212 |
du = [du1(tMsk)', du2(tMsk)', du3(tMsk)']; |
|
|
213 |
else |
|
|
214 |
dv = [dv1(tMsk), dv2(tMsk), dv3(tMsk)]; |
|
|
215 |
du = [du1(tMsk), du2(tMsk), du3(tMsk)]; |
|
|
216 |
end |
|
|
217 |
|
|
|
218 |
[intMatrix(tMsk), intSurface] = TriangleIntersection3D_Moller(... |
|
|
219 |
V1(face1,:), V2(face1,:), V3(face1,:), N1(face1,:), d1(face1,:), dv, ... |
|
|
220 |
U1(face2,:), U2(face2,:), U3(face2,:), N2(face2,:), d2(face2,:), du, ... |
|
|
221 |
getIntersection, debug); |
|
|
222 |
case 'rapid' |
|
|
223 |
% Undocumented experimental feature. In some experiments I got |
|
|
224 |
% identical results as with Moller algorithm, but others gave |
|
|
225 |
% different results. Often faster tham Moller. |
|
|
226 |
intMatrix(tMsk) = TriangleIntersection3D_Rapid( ... |
|
|
227 |
V1(face1,:), V2(face1,:), V3(face1,:), ... |
|
|
228 |
U1(face2,:), U2(face2,:), U3(face2,:), N1(face1,:), N2(face2,:) ); |
|
|
229 |
otherwise |
|
|
230 |
error('Unknown algorithm name'); |
|
|
231 |
end |
|
|
232 |
end % if |
|
|
233 |
|
|
|
234 |
%% Process coplanar triangle pairs. Pass #1: |
|
|
235 |
% compare the overlap of the bounding boxes |
|
|
236 |
tMsk = (intMatrix==-1); |
|
|
237 |
if nnz(tMsk)>0 |
|
|
238 |
[face1, face2] = find(tMsk); |
|
|
239 |
overlap = true; |
|
|
240 |
for idim = 1:3 |
|
|
241 |
v = [V1(face1,idim), V2(face1,idim), V3(face1,idim)]; |
|
|
242 |
u = [U1(face2,idim), U2(face2,idim), U3(face2,idim)]; |
|
|
243 |
t1 = min(v,[],2); |
|
|
244 |
t2 = max(v,[],2); |
|
|
245 |
s1 = min(u,[],2); |
|
|
246 |
s2 = max(u,[],2); |
|
|
247 |
overlap = overlap & (s1<=t2 & t1<=s2); |
|
|
248 |
end |
|
|
249 |
% if overlap intMatrix will remain "-1" otherwise it will change to "0" |
|
|
250 |
intMatrix(tMsk) = -1*overlap; |
|
|
251 |
clear v u t1 t2 s1 s2 overlap |
|
|
252 |
end |
|
|
253 |
|
|
|
254 |
%% Process coplanar triangle pairs. Pass #2: |
|
|
255 |
% use edge-edge intersections |
|
|
256 |
tMsk = (intMatrix==-1); |
|
|
257 |
if nnz(tMsk)>0 |
|
|
258 |
[face1, face2] = find(tMsk); |
|
|
259 |
|
|
|
260 |
% repack data prior to function call |
|
|
261 |
V(:,:,1)=V1(face1,:); V(:,:,2)=V2(face1,:); V(:,:,3)=V3(face1,:); |
|
|
262 |
U(:,:,1)=U1(face2,:); U(:,:,2)=U2(face2,:); U(:,:,3)=U3(face2,:); |
|
|
263 |
[intMatrix(tMsk), intSurface2] = TriangleIntersection2D(V, U, ... |
|
|
264 |
N1(face1,:), getIntersection, debug); |
|
|
265 |
|
|
|
266 |
%% Merge surfaces |
|
|
267 |
if getIntersection |
|
|
268 |
np = size(intSurface.vertices,1); |
|
|
269 |
intSurface.vertices = [intSurface.vertices; intSurface2.vertices]; |
|
|
270 |
intSurface.faces = [intSurface.faces; intSurface2.faces+np]; |
|
|
271 |
intSurface.edges = [intSurface.edges; intSurface2.edges+np]; |
|
|
272 |
if debug |
|
|
273 |
np = size(intSurface.vertices,1); |
|
|
274 |
assert(max(intSurface.faces(:))<=np, 'Bad surface definition') |
|
|
275 |
assert(max(intSurface.edges(:))<=np, 'Bad surface definition') |
|
|
276 |
end |
|
|
277 |
end |
|
|
278 |
end |
|
|
279 |
|
|
|
280 |
%% Clean up the outputs |
|
|
281 |
intMatrix = sparse(double(intMatrix)); |
|
|
282 |
if(getIntersection) |
|
|
283 |
% make point array unique |
|
|
284 |
P = round(intSurface.vertices*PointRoundingTol)/PointRoundingTol; |
|
|
285 |
[~,ia,ic] = unique(P,'rows'); % V = P(ia,:) and P = V(ic,:). |
|
|
286 |
intSurface.vertices = intSurface.vertices(ia,:); |
|
|
287 |
intSurface.faces = ic(intSurface.faces); |
|
|
288 |
intSurface.edges = ic(intSurface.edges); |
|
|
289 |
end |
|
|
290 |
end % function |
|
|
291 |
|
|
|
292 |
%% ======================================================================== |
|
|
293 |
function [iMsk, intSurface] = TriangleIntersection3D_Moller(... |
|
|
294 |
V1, V2, V3, N1, d1, dv, ... |
|
|
295 |
U1, U2, U3, N2, d2, du, ... |
|
|
296 |
getIntersection, debug) |
|
|
297 |
%TriangleIntersection3D tests if 2 triangles defined in 3D intersect. |
|
|
298 |
% This is a secondary test following Tomas Moller algorithm |
|
|
299 |
% |
|
|
300 |
% INPUTS: |
|
|
301 |
% V1, V2, V3, - Nx3 array of surface 1 triangle vertex coordinates |
|
|
302 |
% U1, U2, U3, - Nx3 array of surface 2 triangle vertex coordinates |
|
|
303 |
% N1, d1 - Nx3 array of surface 1 triangle plane equations N1.X-d1=0 |
|
|
304 |
% N2, d2 - Nx3 array of surface 2 triangle plane equations N2.X-d2=0 |
|
|
305 |
% dv - Nx3 array of distances of surface 1 triangle vertices to surface 2 planes |
|
|
306 |
% du - Nx3 array of distances of surface 2 triangle vertices to surface 1 planes |
|
|
307 |
% getIntersection - do we need to output the intersecting surface? |
|
|
308 |
% Algorithm is much simpler if we do not. |
|
|
309 |
% debug - In the debugging mode much more extra "sanity check" test |
|
|
310 |
% are performed. |
|
|
311 |
% |
|
|
312 |
% OUTPUT: |
|
|
313 |
% iMsk - N x 1 intersection boolean mask marking which triangles overlap |
|
|
314 |
% intSurface - intersection surface |
|
|
315 |
% |
|
|
316 |
% ALGORITHM: |
|
|
317 |
% The input triangles are guaranteed to intersect the line of intersection |
|
|
318 |
% of the two planes. Furthermore, these intersections form intervals on |
|
|
319 |
% this line, and the triangles overlap iff these intervals overlap as well. |
|
|
320 |
% Hence, the last part of the algorithm computes a parametric equation |
|
|
321 |
% L(t) of the line of intersection of the two planes, finds the intervals |
|
|
322 |
% (i.e. scalar intervals on L(t)) for which the line lies inside each |
|
|
323 |
% triangle and performs a one-dimensional interval overlap test. |
|
|
324 |
if debug |
|
|
325 |
ok = size(N1,2)==3 && size(N2,2)==3 && size(dv,2)==3 && size(du,2)==3 && ... |
|
|
326 |
size(V1,2)==3 && size(V2,2)==3 && size(V3,2)==3 && ... |
|
|
327 |
size(U1,2)==3 && size(U2,2)==3 && size(U3,2)==3; |
|
|
328 |
assert(ok, 'Incorrect array dimensions'); |
|
|
329 |
end |
|
|
330 |
|
|
|
331 |
%% create strip down versions of MATLAB cross and dot function |
|
|
332 |
cross_prod = @(a,b) [... |
|
|
333 |
a(:,2).*b(:,3)-a(:,3).*b(:,2), ... |
|
|
334 |
a(:,3).*b(:,1)-a(:,1).*b(:,3), ... |
|
|
335 |
a(:,1).*b(:,2)-a(:,2).*b(:,1)]; |
|
|
336 |
dot_prod = @(a,b) a(:,1).*b(:,1)+a(:,2).*b(:,2)+a(:,3).*b(:,3); |
|
|
337 |
normalize = @(V) bsxfun(@rdivide,V, sqrt(sum(V.^2,2))); |
|
|
338 |
|
|
|
339 |
%% Find intervals of surface 1 and 2 triangles |
|
|
340 |
% compute the scalar intervals on L(t) for which the line lies inside each |
|
|
341 |
% triangle |
|
|
342 |
|
|
|
343 |
% Plane creates two open half-spaces. Find the odd vertex, which: |
|
|
344 |
% 1) if no or two vertices are on the plane than pick the vertex which is |
|
|
345 |
% by itself in its half-space |
|
|
346 |
% 2) if one vertex is on the plane and the other two occupy the same |
|
|
347 |
% half-space than pick the vertex on the plane |
|
|
348 |
% 3) if one vertex is on the plane and the other two occupy different |
|
|
349 |
% half-spaces than pick one of the vertices off the plane |
|
|
350 |
% Find vertex using a look-up table "lut" with key calculated based on |
|
|
351 |
% sign of dv and du arrays |
|
|
352 |
lut = [0;3;3;2;1;3;2;2;1;1;2;3;3;0;3;3;2;1;1;2;2;3;1;2;3;3;0]; |
|
|
353 |
n = numel(d1); |
|
|
354 |
rows = (1:n)'; |
|
|
355 |
|
|
|
356 |
%% order surface 1 triangle vertices |
|
|
357 |
a1 = lut(sign(dv)*[9; 3; 1] + 14); % calculate the key and call the look-up table |
|
|
358 |
[b1, c1] = otherDim(a1); |
|
|
359 |
if debug |
|
|
360 |
assert(all(a1>0), 'Something Wrong: triangles are coplanar') |
|
|
361 |
end |
|
|
362 |
a1 = sub2ind([n,3],rows,a1); % convert row and column IDs to array indecies |
|
|
363 |
b1 = sub2ind([n,3],rows,b1); |
|
|
364 |
c1 = sub2ind([n,3],rows,c1); |
|
|
365 |
|
|
|
366 |
%% order surface 2 triangle vertices |
|
|
367 |
a2 = lut(sign(du)*[9; 3; 1] + 14); % calculate the key and call the look-up table |
|
|
368 |
[b2, c2] = otherDim(a2); |
|
|
369 |
if debug |
|
|
370 |
assert(all(a2>0), 'Something Wrong: triangles are coplanar') |
|
|
371 |
end |
|
|
372 |
a2 = sub2ind([n,3],rows,a2); |
|
|
373 |
b2 = sub2ind([n,3],rows,b2); |
|
|
374 |
c2 = sub2ind([n,3],rows,c2); |
|
|
375 |
|
|
|
376 |
%% compute direction of L the line of intersection of 2 planes |
|
|
377 |
% containing 2 triangles. Line L parametric equation: t*D+O=0 |
|
|
378 |
D = cross_prod(N1,N2); % D must be perpendicular to both N1 and N2 |
|
|
379 |
[~, maxDim] = max(abs(D),[],2); % compute and index to the largest component of D |
|
|
380 |
if(getIntersection) |
|
|
381 |
D = normalize(D); |
|
|
382 |
O = zeros(n,3); |
|
|
383 |
d = [d1, d2, zeros(n,1)]; |
|
|
384 |
for r =1:n |
|
|
385 |
N = [N1(r,:); N2(r,:); 0, 0, 0]; |
|
|
386 |
N(3,maxDim(r)) = 1; |
|
|
387 |
dd = d(r,:)'; |
|
|
388 |
O(r,:) = (N\dd)'; %Solve systems of linear equations N*D3 = d for D3 |
|
|
389 |
end |
|
|
390 |
clear N d dd |
|
|
391 |
end |
|
|
392 |
|
|
|
393 |
%% projection of triangle(V1,V2,V3) and triangle(U1,U2,U3) onto intersection line |
|
|
394 |
% Vp and Up are Nx3 arrays with columns indicating corners of triangles 1 and 2 |
|
|
395 |
if(getIntersection) |
|
|
396 |
Vp=[dot_prod(V1-O,D), dot_prod(V2-O,D), dot_prod(V3-O,D)]; |
|
|
397 |
Up=[dot_prod(U1-O,D), dot_prod(U2-O,D), dot_prod(U3-O,D)]; |
|
|
398 |
else |
|
|
399 |
% Project on one of the axis (closest to the intersection line) instead. |
|
|
400 |
% Simplified projection is faster and sufficient if we do not need |
|
|
401 |
% intersection line |
|
|
402 |
idx = sub2ind([n,3],rows,maxDim); |
|
|
403 |
Vp = [V1(idx), V2(idx), V3(idx)]; |
|
|
404 |
Up = [U1(idx), U2(idx), U3(idx)]; |
|
|
405 |
end |
|
|
406 |
clear V1 V2 V3 U1 U2 U3 |
|
|
407 |
|
|
|
408 |
%% Calculate surface 1 and 2 triangle intervals |
|
|
409 |
% t1 and t2 are intersection points of surface 1 with the intersection line |
|
|
410 |
% t*D+O=0, and s1 & s2 are intersection points of surface 2 with the same |
|
|
411 |
% line. Tomas Moller algorithm made this section much more complicated |
|
|
412 |
% trying to avoid divisions. However, I could not detect any speed-up. |
|
|
413 |
% Operations (ADD: 12; MUL:4 ; DIV:4 ) |
|
|
414 |
t1 = Vp(a1) - (Vp(b1)-Vp(a1)).*dv(a1)./(dv(b1)-dv(a1)); |
|
|
415 |
t2 = Vp(a1) - (Vp(c1)-Vp(a1)).*dv(a1)./(dv(c1)-dv(a1)); |
|
|
416 |
s1 = Up(a2) - (Up(b2)-Up(a2)).*du(a2)./(du(b2)-du(a2)); |
|
|
417 |
s2 = Up(a2) - (Up(c2)-Up(a2)).*du(a2)./(du(c2)-du(a2)); |
|
|
418 |
|
|
|
419 |
%% Order the intervals as to t1<t2 and s1<s2 |
|
|
420 |
msk = t2<t1; % order t1 and t2 so t1<t2 |
|
|
421 |
t = t1(msk); t1(msk)=t2(msk); t2(msk)=t; % swap |
|
|
422 |
msk = s2<s1; % order s1 and s2 so s1<s2 |
|
|
423 |
t = s1(msk); s1(msk)=s2(msk); s2(msk)=t; % swap |
|
|
424 |
|
|
|
425 |
%% Perform THE final test we were preparying for. |
|
|
426 |
% It test for the overlap of 2 1D intervals s1->s2 and t1->t2 |
|
|
427 |
iMsk = (s1<t2 & t1<s2); |
|
|
428 |
|
|
|
429 |
%% calculate intersection segments |
|
|
430 |
n = nnz(iMsk); |
|
|
431 |
if(getIntersection && n>0) |
|
|
432 |
% p1 = D*max(t1,s1) + O; p2 = D*min(t2,s2) + O |
|
|
433 |
p1 = bsxfun(@times,D(iMsk,:),max(t1(iMsk),s1(iMsk))) + O(iMsk,:); |
|
|
434 |
p2 = bsxfun(@times,D(iMsk,:),min(t2(iMsk),s2(iMsk))) + O(iMsk,:); |
|
|
435 |
intSurface.vertices = [p1; p2]; |
|
|
436 |
intSurface.faces = [1:n; n+1:2*n; n+1:2*n]'; |
|
|
437 |
intSurface.edges = intSurface.faces(:,1:2); |
|
|
438 |
else |
|
|
439 |
intSurface.vertices = []; |
|
|
440 |
intSurface.faces = []; |
|
|
441 |
intSurface.edges = []; |
|
|
442 |
end % if |
|
|
443 |
end % function |
|
|
444 |
|
|
|
445 |
%% ======================================================================== |
|
|
446 |
function [overlap, intSurface] = TriangleIntersection2D(V, U, N, ... |
|
|
447 |
getIntersection, debug) |
|
|
448 |
% Triangles V(V0,V1,V2) and U(U0,U1,U2) are are coplanar. Do they overlap? |
|
|
449 |
% INPUTS: |
|
|
450 |
% N - array(n,3) of surface normals where V(i,:,:) and U(i,:,:) are on the same plane |
|
|
451 |
% V - array(n,3,3) (nFace x 3 dimensions x 3 vertices) of surface #1 vertices |
|
|
452 |
% U - array(n,3,3) (nFace x 3 dimensions x 3 vertices) of surface #2 vertices |
|
|
453 |
% |
|
|
454 |
% OUTPUT: |
|
|
455 |
% iMsk - N x 1 intersection boolean mask marking which triangles overlap |
|
|
456 |
% intSurface - intersection surface |
|
|
457 |
|
|
|
458 |
|
|
|
459 |
% * parameters: vertices of triangle 1: V0,V1,V2 |
|
|
460 |
% * vertices of triangle 2: U0,U1,U2 |
|
|
461 |
% * result : returns 1 if the triangles intersect, otherwise 0 |
|
|
462 |
|
|
|
463 |
%% Constants needed for creating a mesh based on 3 to 6 points in a circle |
|
|
464 |
tri_mesh{6} = [1 2 6; 2 4 6; 2 3 4; 4 5 6]; |
|
|
465 |
tri_mesh{5} = [1 2 3; 1 3 4; 4 5 1]; |
|
|
466 |
tri_mesh{4} = [1 2 3; 1 3 4]; |
|
|
467 |
tri_mesh{3} = 1:3; |
|
|
468 |
vertices = []; |
|
|
469 |
faces = []; |
|
|
470 |
pairs = []; % each row corresponds to pair of faces. match row number with face number |
|
|
471 |
nVert = 0; |
|
|
472 |
|
|
|
473 |
%% use edge-edge intersections |
|
|
474 |
overlap = false(size(N,1),1); |
|
|
475 |
i1Idx = [1 1 1 2 2 2 3 3 3]; |
|
|
476 |
i2Idx = [3 3 3 1 1 1 2 2 2]; |
|
|
477 |
j1Idx = [1 2 3 1 2 3 1 2 3]; |
|
|
478 |
j2Idx = [3 1 2 3 1 2 3 1 2]; |
|
|
479 |
for row = 1:size(N,1) |
|
|
480 |
% When it is necesary to project 3D plane on 2D, dIdx will be the optimal |
|
|
481 |
% dimensions to use. |
|
|
482 |
[~, a] = max(abs(N(row,:))); |
|
|
483 |
[b, c] = otherDim(a); |
|
|
484 |
dIdx = [b, c]; |
|
|
485 |
order = []; |
|
|
486 |
|
|
|
487 |
%% test all edges of triangle 1 against the edges of triangle 2 |
|
|
488 |
% triangles overlap if edges cross |
|
|
489 |
[edgeMat, P] = EdgesIntersect3D(... |
|
|
490 |
squeeze(V(row,:,i1Idx))',squeeze(V(row,:,i2Idx))', ... |
|
|
491 |
squeeze(U(row,:,j1Idx))',squeeze(U(row,:,j2Idx))'); |
|
|
492 |
overlap(row) = any(edgeMat); |
|
|
493 |
if ~getIntersection && overlap(row), continue; end |
|
|
494 |
|
|
|
495 |
if ~overlap(row) |
|
|
496 |
%% project onto an axis-aligned plane, that maximizes the area |
|
|
497 |
% of the triangles, compute indices: dIdx which correspond to 2 smallest N1 |
|
|
498 |
% components. |
|
|
499 |
V2d = [V(row,dIdx,1); V(row,dIdx,2); V(row,dIdx,3)]; % each row is a 2D vertex |
|
|
500 |
U2d = [U(row,dIdx,1); U(row,dIdx,2); U(row,dIdx,3)]; |
|
|
501 |
|
|
|
502 |
%% test if tri1 is totally contained in tri2 or vice varsa |
|
|
503 |
if PointInTriangle2D(V2d(1,:), U2d) % tri1 is totally contained in tri2 |
|
|
504 |
overlap(row) = true; |
|
|
505 |
order = 1:3; |
|
|
506 |
elseif PointInTriangle2D(U2d(1,:), V2d) % tri2 is totally contained in tri1 |
|
|
507 |
overlap(row) = true; |
|
|
508 |
order = 4:6; |
|
|
509 |
end |
|
|
510 |
if overlap(row) && ~getIntersection, continue; end |
|
|
511 |
clear V2d U2d |
|
|
512 |
end |
|
|
513 |
|
|
|
514 |
%% Build the intersection surface |
|
|
515 |
if getIntersection && overlap(row) |
|
|
516 |
%Assemble all the points which might be needed for desining |
|
|
517 |
%intersection polygon: Intersection points and points from triangle 1 |
|
|
518 |
%and 2 |
|
|
519 |
points = [P(edgeMat,:); squeeze(V(row,:,:))'; squeeze(U(row,:,:))']; |
|
|
520 |
if isempty(order) % when one tri is totally contained in the other tri then order is set |
|
|
521 |
order = IntersectionPolygon(edgeMat>0, points, dIdx, debug); |
|
|
522 |
if isempty(order), continue; end |
|
|
523 |
end |
|
|
524 |
nPoint = length(order); % how many points will be added? |
|
|
525 |
nFace = nPoint-2; % how many faces will be added? |
|
|
526 |
vertices = [vertices; points(order,:)]; %#ok<*AGROW> |
|
|
527 |
faces = [faces; nVert+tri_mesh{nPoint} ]; |
|
|
528 |
pairs = [pairs; row+zeros(nFace,1)]; % each row corresponds to pair of faces. match row number with face number |
|
|
529 |
nVert = nVert + nPoint; |
|
|
530 |
if debug |
|
|
531 |
assert(max(faces(:))<=size(vertices,1), 'Bad surface definition') |
|
|
532 |
end |
|
|
533 |
end |
|
|
534 |
end % for |
|
|
535 |
|
|
|
536 |
%% Prepare outputs |
|
|
537 |
intSurface.vertices = vertices; |
|
|
538 |
intSurface.faces = faces; |
|
|
539 |
if isempty(faces) |
|
|
540 |
intSurface.edges = []; |
|
|
541 |
else |
|
|
542 |
intSurface.edges = [faces(:,1:2); faces(:,2:3); faces(:,[1,3])]; |
|
|
543 |
end |
|
|
544 |
end % function |
|
|
545 |
|
|
|
546 |
%% ======================================================================== |
|
|
547 |
function polygon = IntersectionPolygon(edgeMat, points, dIdx, debug) |
|
|
548 |
% edgeMat is an edge intersection matrix with 3 rows for edges between |
|
|
549 |
% the points 1-3, 1-2, & 2-3 of the triangle 1 and 3 columns for the same |
|
|
550 |
% edges of the triangle 2. If 2 edges intersect a point of intersection |
|
|
551 |
% is calculated and stored in array "points" followed by points of the |
|
|
552 |
% triangles 1 & 2. This function calculates the polygon of the intersection |
|
|
553 |
% between 2 triangles. |
|
|
554 |
|
|
|
555 |
persistent orderLUT verified |
|
|
556 |
if isempty(orderLUT) || isempty(orderLUT{3}) |
|
|
557 |
% This pre-calculated look-up table is used to quickly look up the order of |
|
|
558 |
% the vertices in array "points" which make up polygon of the intersection |
|
|
559 |
% between 2 triangles. A unique key is calculated for each edgeMat using |
|
|
560 |
% dot product between edgeMat(:) and [256 128 64 32 16 8 4 2 1], which is |
|
|
561 |
% used to look up point order around the polygon. Negative numbers in the |
|
|
562 |
% LUT indicate values which were not observed yet so they were not |
|
|
563 |
% independently verified. |
|
|
564 |
% reshape(sprintf('%09s',dec2base(key, 2)),3,3) will convert from the key |
|
|
565 |
% to matrix. |
|
|
566 |
OrderLUT = zeros(432,1); |
|
|
567 |
OrderLUT(003) = 127; |
|
|
568 |
OrderLUT(005) = 128; |
|
|
569 |
OrderLUT(006) = 126; |
|
|
570 |
OrderLUT(009) = 124; |
|
|
571 |
OrderLUT(010) = 1427; |
|
|
572 |
OrderLUT(012) = 1428; |
|
|
573 |
OrderLUT(017) = 1427; |
|
|
574 |
OrderLUT(018) = 124; |
|
|
575 |
OrderLUT(020) = 1426; |
|
|
576 |
OrderLUT(024) = 127; |
|
|
577 |
OrderLUT(027) = 1243; |
|
|
578 |
OrderLUT(029) = 12438; |
|
|
579 |
OrderLUT(030) = 12034; |
|
|
580 |
OrderLUT(033) = 1428; |
|
|
581 |
OrderLUT(034) = 1426; |
|
|
582 |
OrderLUT(036) = 124; |
|
|
583 |
OrderLUT(040) = 128; |
|
|
584 |
OrderLUT(043) = 21834; |
|
|
585 |
OrderLUT(045) = 1243; |
|
|
586 |
OrderLUT(046) = 21349; |
|
|
587 |
OrderLUT(048) = 126; |
|
|
588 |
OrderLUT(051) = 12340; |
|
|
589 |
OrderLUT(053) = 12943; |
|
|
590 |
OrderLUT(054) = 1243; |
|
|
591 |
OrderLUT(065) = 125; |
|
|
592 |
OrderLUT(066) = 1527; |
|
|
593 |
OrderLUT(068) = 1825; |
|
|
594 |
OrderLUT(072) = 123; |
|
|
595 |
OrderLUT(080) = 1327; |
|
|
596 |
OrderLUT(083) = 15234; |
|
|
597 |
OrderLUT(085) = -15234; |
|
|
598 |
OrderLUT(086) = -15243; |
|
|
599 |
OrderLUT(090) = 13247; |
|
|
600 |
OrderLUT(092) = -13247; |
|
|
601 |
OrderLUT(096) = 1328; |
|
|
602 |
OrderLUT(099) = 152834; |
|
|
603 |
OrderLUT(101) = 15234; |
|
|
604 |
OrderLUT(102) = 152349; |
|
|
605 |
OrderLUT(106) = 132847; |
|
|
606 |
OrderLUT(108) = 13247; |
|
|
607 |
OrderLUT(114) = 102347; |
|
|
608 |
OrderLUT(116) = -13247; |
|
|
609 |
OrderLUT(129) = 1527; |
|
|
610 |
OrderLUT(130) = 125; |
|
|
611 |
OrderLUT(132) = 1526; |
|
|
612 |
OrderLUT(136) = 1327; |
|
|
613 |
OrderLUT(139) = 15243; |
|
|
614 |
OrderLUT(141) = 152438; |
|
|
615 |
OrderLUT(142) = 152034; |
|
|
616 |
OrderLUT(144) = 123; |
|
|
617 |
OrderLUT(153) = 12347; |
|
|
618 |
OrderLUT(156) = 123047; |
|
|
619 |
OrderLUT(160) = 1326; |
|
|
620 |
OrderLUT(163) = -152043; |
|
|
621 |
OrderLUT(165) = 13247; |
|
|
622 |
OrderLUT(166) = 15234; |
|
|
623 |
OrderLUT(169) = -182347; |
|
|
624 |
OrderLUT(172) = 193247; |
|
|
625 |
OrderLUT(177) = -132047; |
|
|
626 |
OrderLUT(180) = 13247; |
|
|
627 |
OrderLUT(192) = 127; |
|
|
628 |
OrderLUT(195) = 1243; |
|
|
629 |
OrderLUT(197) = 12438; |
|
|
630 |
OrderLUT(198) = 12034; |
|
|
631 |
OrderLUT(202) = 12364; |
|
|
632 |
OrderLUT(204) = 123648; |
|
|
633 |
OrderLUT(209) = 21364; |
|
|
634 |
OrderLUT(212) = -21364; |
|
|
635 |
OrderLUT(216) = 1243; |
|
|
636 |
OrderLUT(225) = -124638; |
|
|
637 |
OrderLUT(226) = 120364; |
|
|
638 |
OrderLUT(232) = 12438; |
|
|
639 |
OrderLUT(238) = 124356; |
|
|
640 |
OrderLUT(240) = 12034; |
|
|
641 |
OrderLUT(245) = -214356; |
|
|
642 |
OrderLUT(257) = 1528; |
|
|
643 |
OrderLUT(258) = 1526; |
|
|
644 |
OrderLUT(260) = 125; |
|
|
645 |
OrderLUT(264) = 1328; |
|
|
646 |
OrderLUT(267) = -152438; |
|
|
647 |
OrderLUT(269) = 15243; |
|
|
648 |
OrderLUT(270) = -152943; |
|
|
649 |
OrderLUT(272) = 1326; |
|
|
650 |
OrderLUT(275) = 152340; |
|
|
651 |
OrderLUT(277) = 152943; |
|
|
652 |
OrderLUT(278) = 15243; |
|
|
653 |
OrderLUT(281) = 182347; |
|
|
654 |
OrderLUT(282) = -103247; |
|
|
655 |
OrderLUT(288) = 123; |
|
|
656 |
OrderLUT(297) = 12347; |
|
|
657 |
OrderLUT(298) = -123947; |
|
|
658 |
OrderLUT(305) = 123947; |
|
|
659 |
OrderLUT(306) = 12347; |
|
|
660 |
OrderLUT(320) = 128; |
|
|
661 |
OrderLUT(323) = 21834; |
|
|
662 |
OrderLUT(325) = 1243; |
|
|
663 |
OrderLUT(326) = 21349; |
|
|
664 |
OrderLUT(330) = -123648; |
|
|
665 |
OrderLUT(332) = 12364; |
|
|
666 |
OrderLUT(337) = 183642; |
|
|
667 |
OrderLUT(340) = -129364; |
|
|
668 |
OrderLUT(344) = 21834; |
|
|
669 |
OrderLUT(350) = -124365; |
|
|
670 |
OrderLUT(353) = 12463; |
|
|
671 |
OrderLUT(354) = 136492; |
|
|
672 |
OrderLUT(360) = 1243; |
|
|
673 |
OrderLUT(368) = 12943; |
|
|
674 |
OrderLUT(371) = 126543; |
|
|
675 |
OrderLUT(384) = 126; |
|
|
676 |
OrderLUT(387) = 12340; |
|
|
677 |
OrderLUT(389) = 12943; |
|
|
678 |
OrderLUT(390) = 1243; |
|
|
679 |
OrderLUT(394) = -103642; |
|
|
680 |
OrderLUT(396) = 129364; |
|
|
681 |
OrderLUT(401) = 123640; |
|
|
682 |
OrderLUT(404) = 12364; |
|
|
683 |
OrderLUT(408) = 12340; |
|
|
684 |
OrderLUT(413) = 215643; |
|
|
685 |
OrderLUT(417) = -136492; |
|
|
686 |
OrderLUT(418) = 12463; |
|
|
687 |
OrderLUT(424) = 13492; |
|
|
688 |
OrderLUT(427) = -213456; |
|
|
689 |
OrderLUT(432) = 1342; |
|
|
690 |
|
|
|
691 |
% Convert to more convinient format |
|
|
692 |
orderLUT = cell(size(OrderLUT)); |
|
|
693 |
for i = 1:size(OrderLUT,1) |
|
|
694 |
polygon = abs(OrderLUT(i)); |
|
|
695 |
if polygon>0 |
|
|
696 |
polygon = num2str(polygon)-48; % Convert from a single number to array of digits |
|
|
697 |
polygon(polygon==0) = 10; % 0 stands for 10 |
|
|
698 |
orderLUT{i} = polygon; |
|
|
699 |
end |
|
|
700 |
end |
|
|
701 |
% Negative numbers in the LUT indicate values which were not observed yet |
|
|
702 |
% so they were not independently verified. |
|
|
703 |
verified = OrderLUT>0; |
|
|
704 |
clear OrderLUT |
|
|
705 |
end |
|
|
706 |
|
|
|
707 |
%% Calculate unique key for each edgeMat configuration |
|
|
708 |
key = dot(1*edgeMat(:)', [256 128 64 32 16 8 4 2 1]); |
|
|
709 |
assert(key<=432, 'Error: in IntersectionPolygon: key is out of bound'); |
|
|
710 |
|
|
|
711 |
%% Look up the point order around the polygon |
|
|
712 |
polygon = orderLUT{key}; |
|
|
713 |
if (isempty(polygon)) |
|
|
714 |
return |
|
|
715 |
end |
|
|
716 |
|
|
|
717 |
%% in a rare case of 2 intersections there is ambiguity if one or two |
|
|
718 |
% vertices of the triangle lay inside the other triangle. OrderLUT stores |
|
|
719 |
% only the single vertex cases. |
|
|
720 |
nx = nnz(edgeMat(:)); |
|
|
721 |
if nx==2 |
|
|
722 |
pList = polygon; % list of vertices to check |
|
|
723 |
pList(pList<=nx) = []; % keep only the triangle points of the polygon |
|
|
724 |
flip = false; % was there a flip from single vertex to vertices case? |
|
|
725 |
for ip = 1:length(pList) |
|
|
726 |
p = pList(ip); % point to check |
|
|
727 |
t = floor((p-nx-1)/3); % does it belong to triangle 0 or 1 (actually 1 or 2) |
|
|
728 |
tri = (1:3) + nx + 3*abs(1-t); % Points belonging to the other triangle |
|
|
729 |
if ~PointInTriangle2D(points(p,dIdx), points(tri,dIdx)) |
|
|
730 |
d = nx+t*3; % offset |
|
|
731 |
% "p-d" is vertex number of point just tested: 1, 2, or 3. "b, c" are |
|
|
732 |
% the other 2 vertices |
|
|
733 |
[b, c] = otherDim(p-d); |
|
|
734 |
polygon = [polygon(polygon~=p), b+d, c+d]; % remove i2 and add i0 and i1 |
|
|
735 |
flip = true; |
|
|
736 |
end |
|
|
737 |
end |
|
|
738 |
if flip |
|
|
739 |
% if ther were any flips than use existing codes to figure out the |
|
|
740 |
% order of the points around the polygon |
|
|
741 |
DT = delaunayTriangulation(points(polygon,dIdx)); |
|
|
742 |
idx = freeBoundary(DT)'; |
|
|
743 |
idx(2,:) = []; |
|
|
744 |
polygon = polygon(idx); |
|
|
745 |
end |
|
|
746 |
end |
|
|
747 |
|
|
|
748 |
%% Check to duplicate points |
|
|
749 |
tol = 1e6; |
|
|
750 |
P = round(points(polygon,:)*tol)/tol; |
|
|
751 |
[~,ia] = unique(P,'rows'); % V = P(ia,:) and P = V(ic,:). |
|
|
752 |
polygon = polygon(sort(ia)); |
|
|
753 |
|
|
|
754 |
%% Test the results using more expensive function |
|
|
755 |
doPlot = (~verified(key)); |
|
|
756 |
if debug && length(polygon)>3 |
|
|
757 |
DT = delaunayTriangulation(points(polygon,dIdx)); |
|
|
758 |
idx = freeBoundary(DT)'; |
|
|
759 |
idx(2,:) = []; |
|
|
760 |
k = max(abs(diff(idx))); |
|
|
761 |
%doPlot = (k>1 && k<(length(idx)-1)) || (~verified(key)); |
|
|
762 |
assert(k==1 || k==(length(idx)-1), 'Two triangle intersection polygon is not convex') |
|
|
763 |
end |
|
|
764 |
if debug && doPlot % plot the interesting cases |
|
|
765 |
PlotTwoTriangles(points, polygon, 'm') |
|
|
766 |
title(sprintf('key = %i', key)); |
|
|
767 |
end |
|
|
768 |
|
|
|
769 |
end % function |
|
|
770 |
|
|
|
771 |
%% ======================================================================== |
|
|
772 |
function PlotTwoTriangles(points, polygon, color) |
|
|
773 |
% Plotting function used for debugging |
|
|
774 |
nx = size(points,1)-6; |
|
|
775 |
d = (max(points,[],1)-min(points,[],1))/200; |
|
|
776 |
figure(2) |
|
|
777 |
clf |
|
|
778 |
hold on |
|
|
779 |
line( points(nx+(1:2),1), points(nx+(1:2),2), points(nx+(1:2),3), 'Color', 'g'); |
|
|
780 |
line( points(nx+(2:3),1), points(nx+(2:3),2), points(nx+(2:3),3), 'Color', 'g'); |
|
|
781 |
line( points(nx+[1,3],1), points(nx+[1,3],2), points(nx+[1,3],3), 'Color', 'g'); |
|
|
782 |
line( points(nx+(4:5),1), points(nx+(4:5),2), points(nx+(4:5),3), 'Color', 'b'); |
|
|
783 |
line( points(nx+(5:6),1), points(nx+(5:6),2), points(nx+(5:6),3), 'Color', 'b'); |
|
|
784 |
line( points(nx+[4,6],1), points(nx+[4,6],2), points(nx+[4,6],3), 'Color', 'b'); |
|
|
785 |
plot3( points(:,1), points(:,2), points(:,3), 'm.'); |
|
|
786 |
if (length(polygon)>2) |
|
|
787 |
idx = polygon([1:end, 1]); |
|
|
788 |
plot3( points(idx,1), points(idx,2),points(idx,3), 'Color', color, 'LineWidth', 1); |
|
|
789 |
end |
|
|
790 |
for i = 1:nx+6 |
|
|
791 |
text(points(i,1)+d(1), points(i,2)+d(2), points(i,3), num2str(i)) |
|
|
792 |
end |
|
|
793 |
|
|
|
794 |
end % function |
|
|
795 |
|
|
|
796 |
%% ======================================================================== |
|
|
797 |
function [intersect, X] = EdgesIntersect3D(V1,V2, U1,U2) |
|
|
798 |
%EdgesIntersectPoint3D calculates point of intersection of 2 coplanar |
|
|
799 |
% segments in 3D |
|
|
800 |
% |
|
|
801 |
% INPUTS: |
|
|
802 |
% V1,V2 - 1 x 3 coordinates of endpoints of edge 1 |
|
|
803 |
% U1,U2 - 1 x 3 coordinates of endpoints of edge 2 |
|
|
804 |
% OUTPUT: |
|
|
805 |
% X - 1 x 3 coordinates of the intersection point |
|
|
806 |
A = V2-V1; |
|
|
807 |
B = U1-U2; |
|
|
808 |
C = U1-V1; |
|
|
809 |
%% Solve system of equations [A,B,1] * [d;e;0] = C for d and e |
|
|
810 |
det3 = @(a,b) ... % determinant of a matrix with columns: [a, b, 1] |
|
|
811 |
a(:,1).*b(:,2)-a(:,3).*b(:,2) + ... |
|
|
812 |
a(:,2).*b(:,3)-a(:,2).*b(:,1) + ... |
|
|
813 |
a(:,3).*b(:,1)-a(:,1).*b(:,3); |
|
|
814 |
f=det3(A,B); % https://en.wikipedia.org/wiki/Cramer%27s_rule#Explicit_formulas_for_small_systems |
|
|
815 |
t=det3(C,B)./f; % use Cramer's rule |
|
|
816 |
s=det3(A,C)./f; |
|
|
817 |
intersect = (t>=0 & t<=1 & s>=0 & s<=1); |
|
|
818 |
X = V1 + bsxfun(@times,A,t); |
|
|
819 |
end % function |
|
|
820 |
|
|
|
821 |
%% ======================================================================== |
|
|
822 |
function inside = PointInTriangle2D(V1, U) |
|
|
823 |
% check if V1 is inside triangle U (U1,U2,U3) |
|
|
824 |
% Algorithm is checking on which side of the half-plane created by the |
|
|
825 |
% edges the point is. It uses sign of determinant to calculate orientation |
|
|
826 |
% of point triplets. |
|
|
827 |
% INPUTS: |
|
|
828 |
% V1 - 1 x 2 coordinates of a point |
|
|
829 |
% U - 3 x 2 coordinates of endpoints of 3 edges of a triangle |
|
|
830 |
% OUTPUT: |
|
|
831 |
% inside - a boolean or boolean array |
|
|
832 |
det2 = @(A,B,C) (A(:,1)-C(:,1))*(B(:,2)-C(:,2)) - (B(:,1)-C(:,1))*(A(:,2)-C(:,2)); |
|
|
833 |
b1 = (det2(U(1,:), U(2,:), V1) > 0); |
|
|
834 |
b2 = (det2(U(2,:), U(3,:), V1) > 0); |
|
|
835 |
b3 = (det2(U(3,:), U(1,:), V1) > 0); |
|
|
836 |
inside = ((b1 == b2) & (b2 == b3)); % inside if same orientation for all 3 edges |
|
|
837 |
end % function |
|
|
838 |
|
|
|
839 |
%% ======================================================================== |
|
|
840 |
function [b, c] = otherDim(a) |
|
|
841 |
% return set [1 2 3] without k |
|
|
842 |
b = mod(a+1,3)+1; % b and c are vertices which are on the same side of the plane |
|
|
843 |
c = 6-a-b; % a+b+c = 6 |
|
|
844 |
end |
|
|
845 |
|
|
|
846 |
|
|
|
847 |
%% ======================================================================== |
|
|
848 |
function overlap = TriangleIntersection3D_Rapid( v1, v2, v3, u1, u2, u3, n1, n2 ) |
|
|
849 |
%TriangleIntersection3D tests if 2 triangles defined in 3D intersect. |
|
|
850 |
% |
|
|
851 |
% INPUTS: |
|
|
852 |
% v1, v2, v3, - Nx3 array of surface 1 triangle vertex coordinates |
|
|
853 |
% u1, u2, u3, - Nx3 array of surface 2 triangle vertex coordinates |
|
|
854 |
% n1, n2 - Nx3 array of surface 1 & 2 triangle plane normals. Those |
|
|
855 |
% are optional and if provided than the first 2 steps of the algorithm |
|
|
856 |
% (which are equivalent to first 2 steps of Moller algorithm) will be |
|
|
857 |
% skipped. |
|
|
858 |
% |
|
|
859 |
% OUTPUT: |
|
|
860 |
% iMsk - N x 1 intersection boolean mask marking which triangles overlap |
|
|
861 |
% |
|
|
862 |
% ALGORITHM: |
|
|
863 |
% translated from the UNC-CH V-Collide RAPID code |
|
|
864 |
% https://wwwx.cs.unc.edu/~geom/papers/COLLISION/vcol.pdf |
|
|
865 |
|
|
|
866 |
global V1 V2 V3 U1 U2 U3 |
|
|
867 |
|
|
|
868 |
cross_prod = @(a,b) [... |
|
|
869 |
a(:,2).*b(:,3)-a(:,3).*b(:,2), ... |
|
|
870 |
a(:,3).*b(:,1)-a(:,1).*b(:,3), ... |
|
|
871 |
a(:,1).*b(:,2)-a(:,2).*b(:,1)]; |
|
|
872 |
|
|
|
873 |
%% shift t1 and t2 by p1# |
|
|
874 |
V1 = zeros(size(v1)); |
|
|
875 |
V2 = v2-v1; |
|
|
876 |
V3 = v3-v1; |
|
|
877 |
U1 = u1-v1; |
|
|
878 |
U2 = u2-v1; |
|
|
879 |
U3 = u3-v1; |
|
|
880 |
clear v1 v2 v3 u1 u2 u3 |
|
|
881 |
|
|
|
882 |
if(nargin<7) |
|
|
883 |
%% now begin the series of tests |
|
|
884 |
n1 = cross_prod( V2-V1, V3-V1 ); % face normals |
|
|
885 |
n2 = cross_prod( U2-U1, U3-U1 ); % face normals |
|
|
886 |
end |
|
|
887 |
|
|
|
888 |
%% test the face normals |
|
|
889 |
overlap = project6(n1) & project6(n2); |
|
|
890 |
V1 = V1(overlap,:); |
|
|
891 |
V2 = V2(overlap,:); |
|
|
892 |
V3 = V3(overlap,:); |
|
|
893 |
U1 = U1(overlap,:); |
|
|
894 |
U2 = U2(overlap,:); |
|
|
895 |
U3 = U3(overlap,:); |
|
|
896 |
n1 = n1(overlap,:); |
|
|
897 |
n2 = n2(overlap,:); |
|
|
898 |
|
|
|
899 |
%% compute triangle edges |
|
|
900 |
e1 = V2-V1; |
|
|
901 |
e2 = V3-V2; |
|
|
902 |
e3 = V1-V3; |
|
|
903 |
f1 = U2-U1; |
|
|
904 |
f2 = U3-U2; |
|
|
905 |
f3 = U1-U3; |
|
|
906 |
|
|
|
907 |
%% run more tests |
|
|
908 |
overlap2 = project6(cross_prod(e1, f1)); |
|
|
909 |
overlap2 = project6(cross_prod(e1, f2)) & overlap2; |
|
|
910 |
overlap2 = project6(cross_prod(e1, f3)) & overlap2; |
|
|
911 |
overlap2 = project6(cross_prod(e2, f1)) & overlap2; |
|
|
912 |
overlap2 = project6(cross_prod(e2, f2)) & overlap2; |
|
|
913 |
overlap2 = project6(cross_prod(e2, f3)) & overlap2; |
|
|
914 |
overlap2 = project6(cross_prod(e3, f1)) & overlap2; |
|
|
915 |
overlap2 = project6(cross_prod(e3, f2)) & overlap2; |
|
|
916 |
overlap2 = project6(cross_prod(e3, f3)) & overlap2; |
|
|
917 |
overlap2 = project6(cross_prod(e1, n1)) & overlap2; |
|
|
918 |
overlap2 = project6(cross_prod(e2, n1)) & overlap2; |
|
|
919 |
overlap2 = project6(cross_prod(e3, n1)) & overlap2; |
|
|
920 |
overlap2 = project6(cross_prod(f1, n2)) & overlap2; |
|
|
921 |
overlap2 = project6(cross_prod(f2, n2)) & overlap2; |
|
|
922 |
overlap2 = project6(cross_prod(f3, n2)) & overlap2; |
|
|
923 |
overlap(overlap) = overlap2; |
|
|
924 |
end |
|
|
925 |
|
|
|
926 |
%% ======================================================================== |
|
|
927 |
function pass = project6( p ) |
|
|
928 |
% project all 6 vertices of both triangles onto vector p and check if two |
|
|
929 |
% projections overlap |
|
|
930 |
global V1 V2 V3 U1 U2 U3 |
|
|
931 |
dot_prod = @(a,b) a(:,1).*b(:,1)+a(:,2).*b(:,2)+a(:,3).*b(:,3); |
|
|
932 |
%% Project vertices of triangle 1 and find the bounds min1 and max1 |
|
|
933 |
P = [dot_prod(p, V1), dot_prod(p, V2), dot_prod(p, V3)]; |
|
|
934 |
max1 = max(P,[],2); |
|
|
935 |
min1 = min(P,[],2); |
|
|
936 |
%% Project vertices of triangle 2 and find the bounds min1 and max1 |
|
|
937 |
P = [dot_prod(p, U1), dot_prod(p, U2), dot_prod(p, U3)]; |
|
|
938 |
max2 = max(P,[],2); |
|
|
939 |
min2 = min(P,[],2); |
|
|
940 |
%% Compare the bounds to see if they overlap |
|
|
941 |
pass = (( min1 < max2 ) & ( min2 < max1 )) | ~dot_prod(p, p); |
|
|
942 |
end |