function ranged_distance_figure(X, Theta, D)
% Visualizes the pair-wise distance between points and shapes.
%
% Input arguments:
% X:
%    data set with dimensions [n,N]
% Theta:
%    parameter estimates
% D:
%    the matrix of distances [N,N] between points and shapes

% Copyright 2012 Levente Hunyadi

    fig = figure;
    ax = axes('Parent', fig);

    % handle of fitted curve (declaration of variable)
    lsCurve = [];

    % handle of distance ranges
    n = 25;
    lsRanges = zeros(1,n);
    for i = 1 : n
        lsRanges(i) = line(X(1,:), X(2,:), ...
            'Parent', ax, ...
            'Color', 'k', ...
            'LineStyle', 'none', ...
            'Marker', '.', ...
            'HitTest', 'off' ...  % not selectable my mouse click
        );
    end
    
    % scale axes
    xlim([min(X(1,:)), max(X(1,:))]);
    ylim([min(X(2,:)), max(X(2,:))]);
    
    % register callback functions
    set(fig, 'WindowButtonDownFcn', @distance_figure_onmousedown);

    function distance_figure_onmousedown(~, ~)
    % Fired when the user presses the mouse button.
        n = numel(lsRanges);

        cp = get(ax, 'CurrentPoint');
        distsq = sum(bsxfun(@minus, X, [cp(1,1) ; cp(1,2)]).^2, 1);
        [~,ix] = min(distsq);  % point closest to selected

        dist = D(:,ix);  % distance of point to all other points
        [~,dix] = sort(dist);  % distances in increasing order of magnitude
        
        intervals = split_to_intervals(numel(dix), n);  % distance ranges
        
        for k = 1 : n
            nix = dix(intervals(1,k) : intervals(2,k));  % index of points in range interval
            set(lsRanges(k), ...
                'Color', [k/n,k/n,k/n], ...
                'XData', X(1,nix), ...
                'YData', X(2,nix) ...
            );
        end
        
        if ~isempty(lsCurve)
            delete(lsCurve);
        end
        lsCurve = imconic(Theta(:,ix));
    end

    function intervals = split_to_intervals(range, count)
        validateattributes(count, {'numeric'}, {'positive','integer','scalar'});
        validateattributes(range, {'numeric'}, {'positive','integer','scalar','>=',count});
        
        boundaries = floor(linspace(1, range + 1, count + 1));
        boundaries = transpose(boundaries(:));
        intervals = [ boundaries(1 : count) ; boundaries(2 : count + 1) - 1 ];
    end
end
