5 % Given two line segments, L1 and L2,
7 % L1 endpoints: (x1(1),y1(1)) and (x1(2),y1(2))
8 % L2 endpoints: (x2(1),y2(1)) and (x2(2),y2(2))
10 % we can write four equations with four unknowns and then solve them. The
11 % four unknowns are t1, t2, x0 and y0, where (x0,y0) is the intersection of
12 % L1 and L2, t1 is the distance from the starting point of L1 to the
13 % intersection relative to the length of L1 and t2 is the distance from the
14 % starting point of L2 to the intersection relative to the length of L2.
16 % So, the four equations are
18 % (x1(2) - x1(1))*t1 = x0 - x1(1)
19 % (x2(2) - x2(1))*t2 = x0 - x2(1)
20 % (y1(2) - y1(1))*t1 = y0 - y1(1)
21 % (y2(2) - y2(1))*t2 = y0 - y2(1)
23 % Rearranging and writing in matrix form,
25 % [x1(2)-x1(1) 0 -1 0; [t1; [-x1(1);
26 % 0 x2(2)-x2(1) -1 0; * t2; = -x2(1);
27 % y1(2)-y1(1) 0 0 -1; x0; -y1(1);
28 % 0 y2(2)-y2(1) 0 -1] y0] -y2(1)]
30 % Let
's call that A*T = B. We can solve for T with T = A\B. 32 % Once we have our solution we just have to look at t1 and t2 to determine 33 % whether L1 and L2 intersect. If 0 <= t1 < 1 and 0 <= t2 < 1 then the two 34 % line segments cross and we can include (x0,y0) in the output. 36 % In principle, we have to perform this computation on every pair of line 37 % segments in the input data. This can be quite a large number of pairs so 38 % we will reduce it by doing a simple preliminary check to eliminate line 39 % segment pairs that could not possibly cross. The check is to look at the 40 % smallest enclosing rectangles (with sides parallel to the axes) for each 41 % line segment pair and see if they overlap. If they do then we have to 42 % compute t1 and t2 (via the A\B computation) to see if the line segments 43 % cross, but if they don't then the line segments cannot cross. In a
44 % typical application,
this technique will eliminate most of the potential
50 % Adjustments when fewer than five arguments are supplied.
56 self_intersect =
true;
61 self_intersect =
true;
64 self_intersect =
false;
66 self_intersect =
false;
69 % x1 and y1 must be vectors with same number of points (at least 2).
70 if sum(size(x1) > 1) ~= 1 || sum(size(y1) > 1) ~= 1 || ...
71 length(x1) ~= length(y1)
72 error('X1 and Y1 must be equal-length vectors of at least 2 points.')
74 % x2 and y2 must be vectors with same number of points (at least 2).
75 if sum(size(x2) > 1) ~= 1 || sum(size(y2) > 1) ~= 1 || ...
76 length(x2) ~= length(y2)
77 error('X2 and Y2 must be equal-length vectors of at least 2 points.')
81 % Force all inputs to be column vectors.
87 % Compute number of line segments in each curve and some differences we'll
96 % Determine the combinations of i and j where the rectangle enclosing the
97 % i'th line segment of curve 1 overlaps with the rectangle enclosing the
98 % j'th line segment of curve 2.
99 [i,j] = find(repmat(min(x1(1:end-1),x1(2:end)),1,n2) <= ...
100 repmat(max(x2(1:end-1),x2(2:end)).',n1,1) & ...
101 repmat(max(x1(1:end-1),x1(2:end)),1,n2) >= ...
102 repmat(min(x2(1:end-1),x2(2:end)).',n1,1) & ...
103 repmat(min(y1(1:end-1),y1(2:end)),1,n2) <= ...
104 repmat(max(y2(1:end-1),y2(2:end)).',n1,1) & ...
105 repmat(max(y1(1:end-1),y1(2:end)),1,n2) >= ...
106 repmat(min(y2(1:end-1),y2(2:end)).',n1,1));
108 % Force i and j to be column vectors, even when their length is zero, i.e.,
109 % we want them to be 0-by-1 instead of 0-by-0.
113 % Find segments pairs which have at least one vertex = NaN and remove them.
114 % This line is a fast way of finding such segment pairs. We take
115 % advantage of the fact that NaNs propagate through calculations, in
116 % particular subtraction (in the calculation of dxy1 and dxy2, which we
117 % need anyway) and addition.
118 % At the same time we can remove redundant combinations of i and j in the
121 remove = isnan(sum(dxy1(i,:) + dxy2(j,:),2)) | j <= i + 1;
123 remove = isnan(sum(dxy1(i,:) + dxy2(j,:),2));
128 % Initialize matrices. We'll put the T's and B's in matrices and use them
129 % one column at a time. AA is a 3-D extension of A where we'll use one
136 AA([1 3],1,:) = dxy1(i,:).';
137 AA([2 4],2,:) = dxy2(j,:).';
138 B = -[x1(i) x2(j) y1(i) y2(j)].';
140 % Loop through possibilities. Trap singularity warning and then use
141 % lastwarn to see if that plane of AA is near singular. Process any such
142 % segment pairs to determine if they are colinear (overlap) or merely
143 % parallel. That test consists of checking to see if one of the endpoints
144 % of the curve 2 segment lies on the curve 1 segment. This is done by
145 % checking the cross product
147 % (x1(2),y1(2)) - (x1(1),y1(1)) x (x2(2),y2(2)) - (x1(1),y1(1)).
149 % If this is close to zero then the segments overlap.
151 % If the robust option is false then we assume no two segment pairs are
152 % parallel and just go ahead and do the computation. If A is ever singular
153 % a warning will appear. This is faster and obviously you should use it
154 % only when you know you will never have overlapping or parallel segment
158 overlap = false(n,1);
159 warning_state = warning('off','MATLAB:singularMatrix');
160 % Use try-catch to guarantee original warning state is restored.
164 T(:,k) = AA(:,:,k)\B(:,k);
165 [~,last_warn] = lastwarn;
167 if strcmp(last_warn,'MATLAB:singularMatrix')
168 % Force in_range(k) to be false.
170 % Determine if these segments overlap or are just parallel.
171 overlap(k) = rcond([dxy1(i(k),:);xy2(j(k),:) - xy1(i(k),:)]) < eps;
174 warning(warning_state)
176 warning(warning_state)
179 % Find where t1 and t2 are between 0 and 1 and return the corresponding
181 in_range = (T(1,:) >= 0 & T(2,:) >= 0 & T(1,:) <= 1 & T(2,:) <= 1).';
182 % For overlapping segment pairs the algorithm will return an
183 % intersection point that is at the center of the overlapping region.
187 % set x0 and y0 to middle of overlapping region.
188 T(3,overlap) = (max(min(x1(ia),x1(ia+1)),min(x2(ja),x2(ja+1))) + ...
189 min(max(x1(ia),x1(ia+1)),max(x2(ja),x2(ja+1)))).'/2;
190 T(4,overlap) = (max(min(y1(ia),y1(ia+1)),min(y2(ja),y2(ja+1))) + ...
191 min(max(y1(ia),y1(ia+1)),max(y2(ja),y2(ja+1)))).'/2;
192 selected = in_range | overlap;
196 xy0 = T(3:4,selected).';
198 % Remove duplicate intersection points.
199 [xy0,index] = unique(xy0,'rows');
203 % Compute how far along each line segment the
intersections are.
205 sel_index = find(selected);
206 sel = sel_index(index);
207 iout = i(sel) + T(1,sel).';
208 jout = j(sel) + T(2,sel).';
210 else % non-robust option
212 [L,U] = lu(AA(:,:,k));
213 T(:,k) = U\(L\B(:,k));
216 % Find where t1 and t2 are between 0 and 1 and return the corresponding
218 in_range = (T(1,:) >= 0 & T(2,:) >= 0 & T(1,:) < 1 & T(2,:) < 1).';
219 x0 = T(3,in_range).';
220 y0 = T(4,in_range).';
222 % Compute how far along each line segment the
intersections are.
224 iout = i(in_range) + T(1,in_range).';
225 jout = j(in_range) + T(2,in_range).';
229 % Plot the results (useful for debugging).
230 % plot(x1,y1,x2,y2,x0,y0,'ok');
function intersections(in x1, in y1, in x2, in y2, in robust)
Intersections of curves.