function varargout = line_manipulator_gui(varargin)
% LINE_MANIPULATOR_GUI MATLAB code for line_manipulator_gui.fig
%      LINE_MANIPULATOR_GUI, by itself, creates a new LINE_MANIPULATOR_GUI or raises the existing
%      singleton*.
%
%      H = LINE_MANIPULATOR_GUI returns the handle to a new LINE_MANIPULATOR_GUI or the handle to
%      the existing singleton*.
%
%      LINE_MANIPULATOR_GUI('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in LINE_MANIPULATOR_GUI.M with the given input arguments.
%
%      LINE_MANIPULATOR_GUI('Property','Value',...) creates a new LINE_MANIPULATOR_GUI or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before line_manipulator_gui_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to line_manipulator_gui_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help line_manipulator_gui

% Last Modified by GUIDE v2.5 05-Oct-2016 12:11:36

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @line_manipulator_gui_OpeningFcn, ...
                   'gui_OutputFcn',  @line_manipulator_gui_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- Executes just before line_manipulator_gui is made visible.
function line_manipulator_gui_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to line_manipulator_gui (see VARARGIN)

handles.fileReady = false;
handles.imgReady = false;

handles.savePath = '.';
handles.imgFilePath = '.';

handles.pruneLines = [];
handles.mergeLines = [];

handles.ptSize = 8;

handles.angleTol = 35;
handles.densityTol = 0.40;
handles.minLinePts = 20;
handles.roi = [0,0,1,1];

handles.offsetType = 'fixed';
handles.scaleType  = 'anisotropic';

handles.nClusters = 1;

%Optional input to specify line-points file
if nargin>3    
    
    handles.imgFile = varargin{1};
    
    if nargin>4
        imgPath = varargin{2};        
        handles.imgFilePath = imgPath;
    end
    
    if nargin>5
        handles.savePath = varargin{3};
    end
        
    handles.filename = regexprep(handles.imgFile,'\..*','.mat');
    
    set(handles.text_img_filename, 'String', handles.imgFile);
    %set(handles.text_filename, 'String', handles.filename);

    handles.imgReady = true; 
    %handles.fileReady = true;   
    
end

% Choose default command line output for line_manipulator_gui
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes line_manipulator_gui wait for user response (see UIRESUME)
% uiwait(handles.figure1);


% --- Outputs from this function are returned to the command line.
function varargout = line_manipulator_gui_OutputFcn(hObject, eventdata, handles) 
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout{1} = handles.output;



function edit_merge_lines_Callback(hObject, eventdata, handles)
% hObject    handle to edit_merge_lines (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_merge_lines as text
%        str2double(get(hObject,'String')) returns contents of edit_merge_lines as a double
if regexp(get(hObject,'String'), '[^\d,]')
    error('LineManipGui:InvalidLineIdxs', 'Invalid line indices provided');
end

lineIdxs = strsplit(get(hObject,'String'), ',');
    
handles.mergeLines = str2double(lineIdxs);

guidata(hObject, handles);


% --- Executes during object creation, after setting all properties.
function edit_merge_lines_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_merge_lines (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end



function edit_prune_lines_Callback(hObject, eventdata, handles)
% hObject    handle to edit_prune_lines (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_prune_lines as text
%        str2double(get(hObject,'String')) returns contents of edit_prune_lines as a double
if regexp(get(hObject,'String'), '[^\d,]')
    error('LineManipGui:InvalidLineIdxs', 'Invalid line indices provided');
end

lineIdxs = strsplit(get(hObject,'String'), ',');
    
handles.pruneLines = str2double(lineIdxs);

guidata(hObject, handles);


% --- Executes during object creation, after setting all properties.
function edit_prune_lines_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_prune_lines (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton_highlight_merges.
function pushbutton_highlight_merges_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_highlight_merges (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.fileReady 

    highlight_merges(handles);    
    
end

% --- Executes on button press in pushbutton_highlight_prunes.
function pushbutton_highlight_prunes_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_highlight_prunes (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.fileReady 

    highlight_prunes(handles);    
    
end


% --- Executes on button press in pushbutton_merge.
function pushbutton_merge_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_merge (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.fileReady         
    
    if length(handles.mergeLines) < 2
        return;
    end
    
    handles.nPts = size(handles.pts, 1);
    selMask = false(handles.nPts, 1);
    
    ptIdx = 1;
    lineIdx = 1;
    [nGroups, maxLines] = size(handles.lineSizeMat);
    for i = 1:nGroups,

        nLines = nnz(handles.lineSizeMat(i,:));
        for j = 1:nLines,

            nLinePts = handles.lineSizeMat(i,j);                         
            
            if handles.mergeLines(1) == lineIdx 
                insertIdx = ptIdx + nLinePts;               
                insertLine  = j;
            elseif any(handles.mergeLines(2:end) == lineIdx) 
                %select all merge points (except first line segment)
                selMask(ptIdx: ptIdx + nLinePts - 1, :) = true(nLinePts,1);        
                handles.lineSizeMat(i,j) = 0;                   
            end
            ptIdx = ptIdx + nLinePts;
            lineIdx = lineIdx + 1;

        end
        
        handles.lineSizeMat(i,insertLine) = nnz(selMask) + handles.lineSizeMat(i,insertLine);
        handles.lineSizeMat(i,:) = [handles.lineSizeMat(i, handles.lineSizeMat(i,:)>0), zeros(1, maxLines- nnz(handles.lineSizeMat(i,:)))];

    end
    
    %merge all specified line segments at position of first line index
    selPts = handles.pts(selMask,:);
    handles.pts(selMask,:) = NaN; %mark for deletion after storing
    handles.pts = [handles.pts(1:insertIdx-1, :); selPts; handles.pts(insertIdx:end,:)];    
    
    [~,c] = find(handles.lineSizeMat>0);
    handles.lineSizeMat = handles.lineSizeMat(:, 1:max(c));
    handles.pts(isnan(handles.pts(:,1)), :) = [];    
    
    plot_line_segments(handles);

    guidata(hObject, handles);

end


% --- Executes on button press in pushbutton_prune.
function pushbutton_prune_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_prune (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.fileReady 

    if isempty(handles.pruneLines)
        return;
    end
    
    handles = prune_line_segments(handles, handles.pruneLines);

    plot_line_segments(handles);

    guidata(hObject, handles);

end


% --- Executes on button press in pushbutton_choose_file.
function pushbutton_choose_file_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_choose_file (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
[handles.filename, filePath] = uigetfile;
set(handles.text_filename, 'String', handles.filename);

handles.fileReady = true;

%load-in line points and line size matrix
load(fullfile(filePath, handles.filename), 'pts', 'lineSizeMat');
handles.pts = pts;
handles.lineSizeMat = lineSizeMat;

%plot all line segments
plot_line_segments(handles);

guidata(hObject, handles);


% -- Highlights line segments for merge
function highlight_merges(handles)

[selPts, otrPts] = divide_lines(handles, handles.mergeLines);

axes(handles.axes_line_segments);
scatter(selPts(:,1), selPts(:,2), handles.ptSize, 'g');
hold on;
scatter(otrPts(:,1), otrPts(:,2), handles.ptSize, 'b');
hold on;
label_line_segments(handles);
hold off;


% -- Highlights line segments for prune
function highlight_prunes(handles)

[selPts, otrPts] = divide_lines(handles, handles.pruneLines);

axes(handles.axes_line_segments);
scatter(selPts(:,1), selPts(:,2), handles.ptSize, 'r');
hold on;
scatter(otrPts(:,1), otrPts(:,2), handles.ptSize, 'b');
hold on;
label_line_segments(handles);
hold off;


% -- Prune all selected line segments
function handles = prune_line_segments(handles, lineIdxs)

ptIdx = 1;
lineIdx = 1;
[nGroups, maxLines] = size(handles.lineSizeMat);
for i = 1:nGroups,

    nLines = nnz(handles.lineSizeMat(i,:));
    for j = 1:nLines,

        nLinePts = handles.lineSizeMat(i,j); 
        if any(lineIdxs == lineIdx) 
            handles.pts(ptIdx: ptIdx + nLinePts - 1, :) = nan(nLinePts,2);        
            handles.lineSizeMat(i,j) = 0;                                            
        end
        ptIdx = ptIdx + nLinePts;
        lineIdx = lineIdx + 1;

    end

    handles.lineSizeMat(i,:) = [handles.lineSizeMat(i, handles.lineSizeMat(i,:)>0), zeros(1, maxLines- nnz(handles.lineSizeMat(i,:)))];

end

[~,c] = find(handles.lineSizeMat>0);
handles.lineSizeMat = handles.lineSizeMat(:, 1:max(c));
handles.pts(isnan(handles.pts(:,1)), :) = [];    


% --  Plots all line segments
function plot_line_segments(handles)
    
axes(handles.axes_line_segments);
scatter(handles.pts(:,1), handles.pts(:,2), handles.ptSize, 'b');
hold on;
label_line_segments(handles);
hold off;


% -- Label all line segments
function label_line_segments(handles)

ptIdx = 1;
lineIdx = 1;
[nGroups, ~] = size(handles.lineSizeMat);
for i = 1:nGroups,
    
    nLines = nnz(handles.lineSizeMat(i,:));
    for j = 1:nLines,
       
        nLinePts = handles.lineSizeMat(i,j);                
        x = handles.pts(ptIdx + nLinePts - 1, 1);
        y = handles.pts(ptIdx + nLinePts - 1, 2);
        text(x, y, num2str(lineIdx));
        
        ptIdx = ptIdx + nLinePts;
        lineIdx = lineIdx + 1;
        
    end    
    
end



% -- Divide selected line points
function [selPts, otrPts] = divide_lines(handles, lineIdxs)

handles.nPts = size(handles.pts, 1);
selMask = false(handles.nPts, 1);

ptIdx = 1;
lineIdx = 1;
[nGroups, ~] = size(handles.lineSizeMat);
for i = 1:nGroups,
    
    nLines = nnz(handles.lineSizeMat(i,:));
    for j = 1:nLines,
       
        nLinePts = handles.lineSizeMat(i,j);                
        if any(lineIdxs == lineIdx) 
            selMask(ptIdx: ptIdx + nLinePts - 1, :) = true(nLinePts,1);                                               
        end
        ptIdx = ptIdx + nLinePts;
        lineIdx = lineIdx + 1;
        
    end    
    
end

selPts = handles.pts(selMask,:); %selected line segments
otrPts = handles.pts(~selMask,:); %other line segments


% --- Executes on button press in pushbutton_savefile.
function pushbutton_savefile_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_savefile (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.fileReady

    manipFileName = sprintf('manip_%s', handles.filename);
    pts = handles.pts;
    lineSizeMat = handles.lineSizeMat;
    scale  = handles.scale;
    offset = handles.offset;
    fprintf('saving to file: %s/%s\n', handles.savePath, manipFileName);
    save(fullfile(handles.savePath, manipFileName), 'pts', 'lineSizeMat', 'scale', 'offset');

end



function edit_density_tol_Callback(hObject, eventdata, handles)
% hObject    handle to edit_density_tol (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_density_tol as text
%        str2double(get(hObject,'String')) returns contents of edit_density_tol as a double
handles.densityTol = str2double(get(hObject,'String'));

if handles.densityTol < 0 || handles.densityTol > 1
    handles.densityTol = 0.75;
    set(handles.edit_density_tol, 'String', handles.densityTol);
    error('LineManipGui:InvalidDensityTol', 'Density tolerance must be in range [0,1]');
end

guidata(hObject, handles);



% --- Executes during object creation, after setting all properties.
function edit_density_tol_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_density_tol (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end



function edit_angle_tol_Callback(hObject, eventdata, handles)
% hObject    handle to edit_angle_tol (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_angle_tol as text
%        str2double(get(hObject,'String')) returns contents of edit_angle_tol as a double
handles.angleTol = str2double(get(hObject,'String'));

if handles.angleTol < 0 || handles.angleTol > 90
    handles.angleTol = 30;
    set(handles.edit_angle_tol, 'String', handles.angleTol);
    error('LineManipGui:InvalidAngleTol', 'Angle tolerance must be in range [0,90]');
end

guidata(hObject, handles);


% --- Executes during object creation, after setting all properties.
function edit_angle_tol_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_angle_tol (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end



function edit_min_line_pts_Callback(hObject, eventdata, handles)
% hObject    handle to edit_min_line_pts (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_min_line_pts as text
%        str2double(get(hObject,'String')) returns contents of edit_min_line_pts as a double
handles.minLinePts = str2double(get(hObject,'String'));

if handles.minLinePts < 0 || handles.minLinePts > 1000
    handles.minLinePts = 20;
    set(handles.edit_angle_tol, 'String', handles.minLinePts);
    error('LineManipGui:InvalidMinLinePts', 'Min. line points must be in range [0,1000]');
end

guidata(hObject, handles);

% --- Executes during object creation, after setting all properties.
function edit_min_line_pts_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_min_line_pts (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton_choose_img.
function pushbutton_choose_img_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_choose_img (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
[handles.imgFile, handles.imgFilePath] = uigetfile;
set(handles.text_img_filename, 'String', handles.imgFile);

handles.filename = regexprep(handles.imgFile,'\..*','.mat');

handles.imgReady = true;

guidata(hObject, handles);


% --- Executes on button press in pushbutton_lsd.
function pushbutton_lsd_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_lsd (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.imgReady
    
    [handles.pts, handles.lineSizeMat, handles.scale, handles.offset] = ...
                                extract_line_points(handles.imgFile, 'dirPath', handles.imgFilePath ... 
                                                   , 'minLinePts', handles.minLinePts    ...%0
                                                   , 'ptsPerLine', 75   ...%75
                                                   , 'decFactor', 3     ...%3
                                                   , 'angleTol', handles.angleTol     ...%20
                                                   , 'densityTol', handles.densityTol  ...%0.5
                                                   , 'scaleMethod', handles.scaleType ... %'anisotropic'
                                                   , 'meanMethod', handles.offsetType ... %'fixed'
                                                   , 'roi', handles.roi ...
                                                   , 'isNorm', true );   
                                               
    %plot all line segments
    plot_line_segments(handles);
    
    handles.fileReady = true;
    
    guidata(hObject, handles);                                               

end



function edit_roi_Callback(hObject, eventdata, handles)
% hObject    handle to edit_roi (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_roi as text
%        str2double(get(hObject,'String')) returns contents of edit_roi as a double
if regexp(get(hObject,'String'), '[^\d,\.\s]')
    error('LineManipGui:InvalidRoiIdxs', 'Invalid roi parameters provided');
end

roiParams = strsplit(get(hObject,'String'), ',');
roiParams = str2double(roiParams);

if any(roiParams<0) || any(roiParams>1)
    error('LineManipGui:InvalidRoiParams', 'Out of bounds roi parameters [0,1]');
end
    
handles.roi = roiParams;

guidata(hObject, handles);


% --- Executes during object creation, after setting all properties.
function edit_roi_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_roi (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in popupmenu_scale.
function popupmenu_scale_Callback(hObject, eventdata, handles)
% hObject    handle to popupmenu_scale (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_scale contents as cell array
%        contents{get(hObject,'Value')} returns selected item from popupmenu_scale
contents = cellstr(get(hObject,'String'));

handles.scaleType = contents{get(hObject,'Value')};

guidata(hObject, handles);

% --- Executes during object creation, after setting all properties.
function popupmenu_scale_CreateFcn(hObject, eventdata, handles)
% hObject    handle to popupmenu_scale (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on selection change in popupmenu_offset.
function popupmenu_offset_Callback(hObject, eventdata, handles)
% hObject    handle to popupmenu_offset (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = cellstr(get(hObject,'String')) returns popupmenu_offset contents as cell array
%        contents{get(hObject,'Value')} returns selected item from popupmenu_offset
contents = cellstr(get(hObject,'String'));

handles.offsetType = contents{get(hObject,'Value')};

guidata(hObject, handles);


% --- Executes during object creation, after setting all properties.
function popupmenu_offset_CreateFcn(hObject, eventdata, handles)
% hObject    handle to popupmenu_offset (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton_cluster_lines.
function pushbutton_cluster_lines_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_cluster_lines (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.fileReady && handles.nClusters > 1
        
    [handles.pts, handles.lineSizeMat] = cluster_line_points(handles.nClusters, handles.pts, handles.lineSizeMat, handles.scale);                
    
    ax = handles.axes_line_segments;
    
    idx = 1;
    colors = jet(handles.nClusters);
    for g = 1:handles.nClusters,        
        nPts = sum(handles.lineSizeMat(g,:));
        hold on; scatter(ax, handles.pts(idx:idx+nPts-1, 1), handles.pts(idx:idx+nPts-1, 2), handles.ptSize, colors(g,:));
        idx = idx + nPts;        
    end    
    
    hold off;
    
    guidata(hObject, handles);        
    
end


function edit_num_clusters_Callback(hObject, eventdata, handles)
% hObject    handle to edit_num_clusters (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_num_clusters as text
%        str2double(get(hObject,'String')) returns contents of edit_num_clusters as a double
handles.nClusters = str2double(get(hObject,'String'));

if handles.nClusters < 0 || handles.nClusters > 10 || handles.nClusters ~= round(handles.nClusters)
    handles.nClusters = 1;
    set(handles.edit_num_clusters, 'String', handles.nClusters);
    error('LineManipGui:InvalidNumClusters', 'Number of clusters must be in an integer in range [1,10]');
end

guidata(hObject, handles);

% --- Executes during object creation, after setting all properties.
function edit_num_clusters_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_num_clusters (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end


% --- Executes on button press in pushbutton_merge_rect_select.
function pushbutton_merge_rect_select_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_merge_rect_select (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.fileReady
   
    %rectangular selection [xmin, ymin, width, height]
    rect = getrect(handles.axes_line_segments);
    
    nTotalLines = nnz(handles.lineSizeMat);
    selectIdxs = nan(nTotalLines, 1);
    
    nSelect = 0;
    ptIdx = 1;
    lineIdx = 1;
    [nGroups, ~] = size(handles.lineSizeMat);
    for i = 1:nGroups,

        nLines = nnz(handles.lineSizeMat(i,:));
        for j = 1:nLines,

            nLinePts = handles.lineSizeMat(i,j);
            linePts = handles.pts(ptIdx:ptIdx + nLinePts - 1, :);
            
            if all(all(linePts > repmat(rect(1:2), [nLinePts,1]))) && ...
               all(all(linePts < repmat(rect(1:2)+rect(3:4), [nLinePts,1])))
                
                nSelect = nSelect + 1;
                selectIdxs(nSelect) = lineIdx;
                
            end
                                      
            ptIdx = ptIdx + nLinePts;
            lineIdx = lineIdx + 1;

        end    

    end   
    
    selectIdxs = selectIdxs(1:nSelect);    
    handles.mergeLines = selectIdxs;
    
    idxStr = strjoin(cellstr(num2str(selectIdxs(:))), ',');        
    set(handles.edit_merge_lines, 'String', idxStr);
    
    guidata(hObject, handles);
    
end

% --- Executes on button press in pushbutton_prune_rect_select.
function pushbutton_prune_rect_select_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_prune_rect_select (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
if handles.fileReady
   
    %rectangular selection [xmin, ymin, width, height]
    rect = getrect(handles.axes_line_segments);
    
    nTotalLines = nnz(handles.lineSizeMat);
    selectIdxs = nan(nTotalLines, 1);
    
    nSelect = 0;
    ptIdx = 1;
    lineIdx = 1;
    [nGroups, ~] = size(handles.lineSizeMat);
    for i = 1:nGroups,

        nLines = nnz(handles.lineSizeMat(i,:));
        for j = 1:nLines,

            nLinePts = handles.lineSizeMat(i,j);
            linePts = handles.pts(ptIdx:ptIdx + nLinePts - 1, :);
            
            if all(all(linePts > repmat(rect(1:2), [nLinePts,1]))) && ...
               all(all(linePts < repmat(rect(1:2)+rect(3:4), [nLinePts,1])))
                
                nSelect = nSelect + 1;
                selectIdxs(nSelect) = lineIdx;
                
            end
                                      
            ptIdx = ptIdx + nLinePts;
            lineIdx = lineIdx + 1;

        end    

    end   
    
    selectIdxs = selectIdxs(1:nSelect);    
    handles.pruneLines = selectIdxs;
    
    idxStr = strjoin(cellstr(num2str(selectIdxs(:))), ',');        
    set(handles.edit_prune_lines, 'String', idxStr);
    
    guidata(hObject, handles);
    
end