function gplotdc(A,xy,varargin)
%GPLOTDC Plot a Directed Graph
% GPLOTDC(A,XY) Plots the Directed Graph represented by adjacency
%   matrix A and points xy using the default style described below
% GPLOTDC(A,XY,PARAM1,VAL1,PARAM2,VAL2,...) Plots the Directed Graph
%   using valid parameter name/value pairs
%
%   Inputs:
%       A     - NxN adjacency matrix, where A(I,J) is nonzero (=1)
%               if and only if there is an edge between points I and J
%       xy    - Nx2 matrix of x/y coordinates
%       ...   - Parameter name/value pairs that are consistent with
%               valid PLOT parameters can also be specified
%
%   Default Plot Style Details:
%       1. Undirected (2-way) edges are plotted as straight solid lines
%       2. Directed (1-way) edges are plotted as curved dotted lines with
%           the curvature bending counterclockwise moving away from a point
%       3. Any vertex that is connected to itself is plotted with a
%           circle around it
%
%   Example:
%       % plot a directed graph using default line styles
%       n = 9; t = 2*pi/n*(0:n-1);
%       A = round(rand(n));
%       xy = [cos(t); sin(t)]';
%       gplotdc(A,xy);
%       for k = 1:n
%           text(xy(k,1),xy(k,2),['  ' num2str(k)],'Color','k', ...
%               'FontSize',12,'FontWeight','b')
%       end
%
%   Example:
%       % plot a directed graph using plot name/value parameter pairs
%       n = 9; t = 2*pi/n*(0:n-1);
%       A = round(rand(n));
%       xy = [cos(t); sin(t)]';
%       gplotdc(A,xy,'LineWidth',2,'MarkerSize',8);
%
%   Example:
%       % plot a directed graph using a different color and linestyle
%       n = 9; t = 2*pi/n*(0:n-1);
%       A = round(rand(n));
%       xy = [cos(t); sin(t)]';
%       gplotdc(A,xy,'Color',[0.67 0 1],'LineStyle',':');
%
% See also: gplot, plot
%
% Author: Joseph Kirk
% Email: jdkirk630@gmail.com
% Release: 1.0
% Release Date: 4/12/08

% Process Inputs
if nargin < 2
    error('Not enough input arguments.');
end
[nr,nc] = size(A);
[n,dim] = size(xy);
if (~n) || (nr ~= n) || (nc ~= n) || (dim < 2)
    eval(['help ' mfilename]);
    error('Invalid input. See help notes above.');
end
params = struct();
for var = 1:2:length(varargin)-1
    params.(varargin{var}) = varargin{var+1};
end

% Parse the Adjacency Matrix
A = double(logical(A));
iA = diag(diag(A));         % self-connecting edges
dA = A.*(1-A');             % directed edges (1-way)
uA = A-dA-iA;               % undirected edges (2-way)

% Make NaN-Separated XY Vectors
[ix,iy] = makeXY(iA,xy);
[dx,dy] = makeXY(dA,xy);
[ux,uy] = makeXY(tril(uA,0),xy);

% Add Curvature to Directed Edges
dX = dx;
dY = dy;
pct = 0.04;
for k = 1:4
    [dX,dY,pct] = makeCurved(dX,dY,pct);
end

% Plot the Graph
plot(ux,uy,'b-',params)
hold on
plot(dX,dY,'r--',params)
plot(ix,iy,'ko',params)
plot(xy(:,1),xy(:,2),'k.')
hold off

    function [x,y] = makeXY(A,xy)
        if any(A(:))
            [J,I] = find(A');
            m = length(I);
            xmat = [xy(I,1) xy(J,1) NaN(m,1)]';
            ymat = [xy(I,2) xy(J,2) NaN(m,1)]';
            x = xmat(:);
            y = ymat(:);
        else
            x = NaN;
            y = NaN;
        end
    end

    function [X,Y,PCT] = makeCurved(x,y,pct)
        N = length(x);
        if N < 2
            X = x;
            Y = y;
        else
            M = 2*N-1;
            X = zeros(1,M);
            Y = zeros(1,M);
            X(1:2:M) = x;
            Y(1:2:M) = y;
            X(2:2:M-1) = 0.5*(x(1:N-1)+x(2:N))+pct*diff(y);
            Y(2:2:M-1) = 0.5*(y(1:N-1)+y(2:N))-pct*diff(x);
        end
        PCT = 0.5*pct;
    end
end
