I need to implement the following logic: Given a set of 2d sample points (given as x-y-coordinate pairs) and a set of line segments (also x-y-coordinate pairs). Edit 1: How to calculate (vectorized) the distance of the points pi to the lines Li?
The points are approximately near the lines and I want to get the distance from each of the sample points to the nearest line segment. The may be points, which are a bit "off" (see p6 in the first picture) and these could be found by the following algorithm:
- Case: The sample points projection to the line segment is "left outside". In this case I need the euclidean distance from p1 to x1.
- Case: The sample points projection to the line segment is "inside" the line segment. In this case I need the distance from p2 to the line from x1 to x2.
- Case: The sample points projection to the line segment is "right outside". In this case I need the euclidean distance from p3 to x2.
There is a vectorized solution (thanks to User Andy) which uses the projection "globally", always assuming case 2 for each point-linesegment-pair. However, this returns the distance [1 1 1]
for p1 ... p3 , where the desired distance would be [1.4142 1 1.4142]
. Can this code be modified to these needs?
ptsx = [1 3 5];
ptsy= [1 1 1];
linesx = [2 4];
linesy = [0 0];
points = [ptsx;ptsy];
lines = [linesx;linesy];
% start of lines
l = [linesx(1:end-1);linesy(1:end-1)];
% vector perpendicular on line
v = [diff(linesy);-diff(linesx)];
% make unit vector
v = v ./ hypot (v(1,:),v(2,:));
v = repmat (v, 1, 1, size (points, 2));
% vector from points (in third dimension) to start of lines (second dimension)
r = bsxfun (@minus, permute (points, [1 3 2]), l);
d = abs (dot (v, r));
dist = squeeze (min (d, [], 2))
Mathematically speaking, the cases can be separated by looking at the length of the projection of vec(pi-x1)
onto vec(x2-x1)
. If this length factor is < 0, then the point is "left" of the line segment, if it is between 0 and 1, perpendicular projection is possible, if it is > 1, then the point is "right" of the line segment.
Edit 1: I will add a pseudocode to explain how this could be solved with a double for-loop, but since I have around 6000 samples and 10000 lines, the loop solution is not an option for me.
for each sample point pi
for each linesegment Li
a = vector from start of Li to end of Li
b = vector from pi to start of Li
relLength = dot(a,b)/norm(a)^2
if relLength < 0: distance = euclidean distance from start of Li to pi
if relLength > 1: distance = euclidean distance from end of Li to pi
else: distance = perpendicular distance from pi to Li
endfor
endfor
Edit 2 / 2017-09-07: I managed to vectorize the first part of this algorithm. relLength now contains the relative length of the projection of each pi-startOfLi
onto each line segment.
ptsx = [0.5 2 3 5.5 8 11];
ptsy= [1 2 -1.5 0.5 4 5];
linesx = [0 2 4 6 10 10 0 0];
linesy = [0 0 0 0 0 4 4 0];
points = [ptsx;ptsy];
lines = [linesx;linesy];
% Start of all lines
L1 = [linesx(1:end-1); linesy(1:end-1)];
% Vector of each line segment
a = [diff(linesx); diff(linesy)];
b = bsxfun(@minus, permute(points, [1 3 2]), L1);
aRep = repmat(a, 1, 1, length(b(1,1,:)));
relLength = dot(aRep,b)./norm(a, 'cols').^2