function [dist, gradient] = Rdist_grad(pts)
% function [D, G] = Rdist_grad(pts)
%   computes the distance field D and gradient G at each point in
%   PTS, an Nx2 array.  This function is pretty slow, mostly
%   because of the distance-to-curve computation, which involves a
%   loop.

x = pts(:, 1);
y = pts(:, 2);

% The R is two rectangles and a 3 segment bezier curve
[r1, gr1] = rect_dist(x, y, 0.2, 0.1, 0.3, 0.90);
[r2, gr2] = rect_dist(x, y, 0.73, 0.091, 0.76, 0.11);
[c1, gc1] = bezier_dist(x, y, 0.37, 0.36, 0.30, 0.3832, 0.32, 0.46, .40, ...
    0.43, 100, 0.01); 
[c2, gc2] = bezier_dist(x, y, 0.40, 0.43, 0.64, 0.34, 0.5, 0.1, 0.75, ...
    0.1, 100, 0.01); 
[c3, gc3] = bezier_dist(x, y, 0.3, 0.89, 0.9, 0.90, 0.75, 0.25, 0.37, ...
    0.36, 180, 0.01); 

[dist, i] = max([r1, r2, c1, c2, c3], [], 2);
grx = [gr1(:, 1) gr2(:, 1) gc1(:, 1) gc2(:, 1) gc3(:, 1)];
gry = [gr1(:, 2) gr2(:, 2) gc1(:, 2) gc2(:, 2) gc3(:, 2)];
s = size(grx);
gradient = [grx(sub2ind(s, (1:s(1))', i)) gry(sub2ind(s, ...
      (1:s(1))', i))];
gradient(isnan(gradient)) = 0;




function [rd, grad] = rect_dist(x, y, x1, y1, x2, y2)
% distance from rectangle function
x1 = repmat(x1, size(x));
y1 = repmat(y1, size(x));
x2 = repmat(x2, size(x));
y2 = repmat(y2, size(x));
pts = [x y];
rd = sum((pts - [x1 y1]).^2, 2);
grad = [x1 y1] - pts;
[rd, grad] = find_closer(rd, grad, [x2 y1] - pts, [x2 y1] - pts);
[rd, grad] = find_closer(rd, grad, [x1 y2] - pts, [x1 y2] - pts);
[rd, grad] = find_closer(rd, grad, [x2 y2] - pts, [x2 y2] - pts);
in = find((x > x1) & (x < x2));
if (~ isempty(in)),
  
  [rd(in), grad(in, :)] = find_closer(rd(in), grad(in, :), [x(in) y1(in)] - pts(in, :), repmat([0 1], size(in), 1));
  [rd(in), grad(in, :)] = find_closer(rd(in), grad(in, :), [x(in) y2(in)] - pts(in, :), repmat([0 -1], size(in), 1));
end
in = find((y > y1) & (y < y2));
if (~ isempty(in)),
  [rd(in), grad(in, :)] = find_closer(rd(in), grad(in, :), [x1(in) y(in)] - pts(in, :), repmat([1 0], size(in), 1));
  [rd(in), grad(in, :)] = find_closer(rd(in), grad(in, :), [x2(in) y(in)] - pts(in, :), repmat([-1 0], size(in), 1));
end
rd = sqrt(rd);
neginds = ((x < x1) | (x > x2) | (y < y1) | (y > y2));
rd(neginds) = - rd(neginds);
wstate = warning('off');
grad = grad ./ repmat(sqrt(sum(grad.^2, 2)), 1, 2);
warning(wstate)

function [d, grad] = find_closer(d, grad, delta, newgrad)
newd = sum(delta.^2, 2);
inds = (newd < d);
d(inds) = newd(inds);
grad(inds, :) = newgrad(inds, :);



function [bd, grad] = bezier_dist(x, y, x1, y1, x2, y2, x3, y3, x4, y4, ...
    steps, radius)
% distance from bezier curve function
dx1 = x1 - x;
dx2 = x2 - x;
dx3 = x3 - x;
dx4 = x4 - x;
dy1 = y1 - y;
dy2 = y2 - y;
dy3 = y3 - y;
dy4 = y4 - y;

bd = sqdist(dx1, dy1);
cp = repmat([x1 y1], size(bd, 1), 1);
for i = 1:steps,
  t = i / steps;
  dists = sqdist(bez4(dx1, dx2, dx3, dx4, t), bez4(dy1, dy2, ...
      dy3, dy4, t));
  inds = find(dists < bd);
  if (~ isempty(inds)),
    bd(inds) = dists(inds);
    cp(inds, 1) = bez4(x1, x2, x3, x4, t);
    cp(inds, 2) = bez4(y1, y2, y3, y4, t);
  end
end
bd = radius - sqrt(bd);
grad = cp - [x y];
wstate = warning('off');
grad = grad ./ repmat(sqrt(sum(grad.^2, 2)), 1, 2);
warning(wstate);


function sd = sqdist(x, y)
sd = x.*x + y.*y;


function b4 = bez4(a, b, c, d, t)
a = linear(a,b,t);
b = linear(b,c,t);
c = linear(c,d,t);
a = linear(a,b,t);
b = linear(b,c,t);
b4 = linear(a,b,t);


function l = linear(a,b,t)
l = a + (b - a) * t;
