classdef SBSRSKETCH
    % the sketch image and associated features
    
    properties
        nameoflbs
        %         cxcubes_per_cxpts_ldir
        %         cxcubes_per_cxpts_label
        %         cxscoremap_per_cxpts_ldir
        %         cxscoremap_per_cxpts_label
        ptsketch
        gdxy
        seglbmap
        segcontmap %segment continuity map
        segcontgroups %group{i}=seglbs that belong to ith continuity group
        segpargroups %group{i}=seglbs that belong to ith parallel group
        segtimegroups %group{i}=seglbs that belong to ith timing group
        seglinegroups %group{i} = seglbs that belong to the same line
        
        segtimemap %segment timing map, segtimemap(i,j)=k iff pixel (i,j) is painted at time k
        
        % lens per 2d seg
        seglens
        
        % for legacy use, the pixels belonging to each dblbs
        inds_per_dblbs
    end
    
    properties
        % binary image, black background and white foreground
        m_sketchimg
        m_gdx %gradient map x
        m_gdy %gradient map y
        m_seglbs_per_sketchpts
        m_parallelgroup_sketchpts2sketchpts %adjmat(i,j)==1 if pixel(i) and pixel(j) are in the same parallel group
        m_parallelgroup_seg2seg; %adjmat(i,j)==1 iff seglb(i) and seglb(j) is in the same parallel group
        
        m_segtimes %segtimes(i) = the timing of segment i, nan if the timing is not available
        m_segcontgroup_seglb2seglb %segcontgroup(i,j)=k if segment i and segment j originate from a continuous stroke k
        
        % extracted features
        
        % sparse features
        % line context
        m_cxpts %context feature sample points
        
        % candidate pivot points
        m_candpivots
        
        %list of 3x3 transmat that transforms each candidate 2d segment to target location
        %target_segpts = dftransmat * dbobj.segmentlist_imgc{i}.ptsketch
        m_dftransmat_per_candpivot
        m_segid_per_candpivot %[idsrc,idseg]' per candpivot
        m_lbs_per_candpivot
        m_candpivot2cxpts
        
        %if symask(i,j)==1, candpivot(i) and candpivot(j) is linked by symmetry
        m_symask_candpivot2candpivot
        
        %the component at candpivot is forced to be fully covered by sketch
        m_candpivot2cxpts_fullcover
        m_candpivot2cxpts_justenough
        
        m_matchscore_per_candpivot
        m_cxcubes_per_candpivot_ldir
        m_cxcubes_per_candpivot_label
        
        %context score per candpivot obtained by matching
        m_cxscore_per_candpivot_ldir
        m_cxscore_per_candpivot_label
        
        % -------- data after matching -----------------
        % matchdata per database obj
        m_matchdatalist
        
        m_dblbs %all possible labels
        
        %max degree, maxdgmat(i,j) = number of components dblbs(j) can be
        %connected to dblbs(i)
        m_maxdgmat
        
        % known symmetry plane
        m_symplane_p0
        m_symplane_normal
        
        % original data
        m_datafile
        m_original_sketchimg
        
        % ground truth labeling, inds{i} = pixels for label dblbs(i)
        m_inds_per_dblbs
        
        % ground truth segmentation, inds{i} = pixels for ith component
        m_inds_per_component
        m_label_per_component % val(i) is the label of ith component
        
        % user specified (SBSR) view
        % the (SBSR) view in use
        m_view
        m_view_auto %automatically found view
        m_view_manual %manually specified view
        
        % the obj file associated with autoview
        m_autoview_objfile
        
        % view in [az,el], used for view determination
        m_azel
        
        % options used in initialization
        m_sketchopt
        
        % for vector image, this is the original points and directions for each seglb
        % the points are sequenced such that m_pts_per_seglb{i}'s end point
        % can connect to m_pts_per_seglb{i+1}'s beginning point if they
        % belong to a stroke
        m_vecpts_per_seglb
        m_vecdirs_per_seglb
        m_vecpts_per_line
        m_vecimg %the vector image
        
        % sequenced image points for each seglb
        m_imgpts_per_seglb
        m_imgpts_per_line
        m_imgpts_per_stroke
        
        % sequence map
        % m_seqmap(i)<m_seqmap(j) if point i is drawn earlier than point j
        m_seqmap
        
    end
    
    methods
        function val = get.inds_per_dblbs(obj)
            val = [];
            if ~isempty(obj.m_label_per_component) && ~isempty(obj.m_inds_per_component) && ~isempty(obj.m_dblbs)
                val = cell(1,length(obj.m_dblbs));
                for i=1:length(obj.m_dblbs)
                    thislb = obj.m_dblbs(i);
                    mask = obj.m_label_per_component == thislb;
                    if any(mask)
                        indslist = obj.m_inds_per_component(mask);
                        indslist = cellfun(@(x)x(:)',indslist,'uniformoutput',false);
                        inds = cat(2,indslist{:});
                        val{i} = inds;
                    end
                end
            end
        end
        
        function val = get.seglens(obj)
            val = [];
            if ~isempty(obj.m_seglbs_per_sketchpts)
                seglbs = unique(obj.m_seglbs_per_sketchpts);
                lens = histc(obj.m_seglbs_per_sketchpts,seglbs);
                val = zeros(1,max(seglbs));
                val(seglbs) = lens;
                val = val(:)';
            end
        end
        
        function val = get.seglinegroups(obj)
            val = {};
            if ~isempty(obj.m_imgpts_per_line)
                val = cell(1,length(obj.m_imgpts_per_line));
                linelbmap = zeros(size(obj.m_sketchimg));
                for i=1:length(obj.m_imgpts_per_line)
                    imgpts = obj.m_imgpts_per_line{i};
                    inds = sub2ind(size(obj.m_sketchimg),imgpts(1,:),imgpts(2,:));
                    linelbmap(inds) = i;
                end
                
                nseglb = max(obj.m_seglbs_per_sketchpts);
                indsall = find(obj.m_sketchimg);
                for i=1:nseglb
                    mask = obj.m_seglbs_per_sketchpts == i;
                    if ~any(mask)
                        continue;
                    end
                    seginds = indsall(mask);
                    lineidx = linelbmap(seginds);
                    lineidx = mode(lineidx);
                    val{lineidx}(end+1) = i;
                end
            end
        end
        
        function val = get.segtimegroups(obj)
            val = {};
            if ~isempty(obj.m_segtimes)
                nstep = 2;
                segtimes_trunc = floor(obj.m_segtimes);
                maxgroupid = max(segtimes_trunc)-nstep+1;
                val = cell(1,maxgroupid);
                for i=1:maxgroupid
                    mask = (segtimes_trunc <= i+nstep-1) & (segtimes_trunc >= i);
                    seglbs = find(mask);
                    val{i} = seglbs(:)';
                end
            end
        end
        
        function val = get.segpargroups(obj)
            val = {};
            if ~isempty(obj.m_parallelgroup_seg2seg)
                maxgroupid = max(obj.m_parallelgroup_seg2seg(:));
                val = cell(1,maxgroupid);
                for i=1:maxgroupid
                    [is,~] = find(obj.m_parallelgroup_seg2seg==i);
                    seglbs = unique(is);
                    val{i} = seglbs(:)';
                end
                
                maskempty = cellfun(@(x)isempty(x),val);
                val = val(~maskempty);
            end
        end
        
        function val = get.segtimemap(obj)
            val = [];
            if ~isempty(obj.m_segtimes)
                % relabel each time step with integer
                [~,segtimes] = sort(obj.m_segtimes);
                
                val = zeros(size(obj.m_sketchimg));
                inds = find(obj.m_sketchimg);
                for i=1:length(segtimes)
                    mask = obj.m_seglbs_per_sketchpts == i;
                    val(inds(mask)) = segtimes(i);
                end
            end
        end
        
        function val = get.segcontgroups(obj)
            val = {};
            if ~isempty(obj.m_segcontgroup_seglb2seglb)
                maxgroupid = max(obj.m_segcontgroup_seglb2seglb(:));
                val = cell(1,maxgroupid);
                for i=1:maxgroupid
                    [is,~] = find(obj.m_segcontgroup_seglb2seglb==i);
                    seglbs = unique(is);
                    val{i} = seglbs(:)';
                end
            end
        end
        
        function val = get.segcontmap(obj)
            val = [];
            if ~isempty(obj.m_segcontgroup_seglb2seglb)
                contgroups = obj.segcontgroups;
                inds = find(obj.m_sketchimg);
                val = zeros(size(obj.m_sketchimg));
                for i=1:length(contgroups)
                    seglbs = contgroups{i};
                    maskhit = ismember(obj.m_seglbs_per_sketchpts,seglbs);
                    val(inds(maskhit)) = i;
                end
            end
        end
        
        function val = get.seglbmap(obj)
            val = [];
            if ~isempty(obj.m_seglbs_per_sketchpts)
                val = zeros(size(obj.m_sketchimg));
                val(obj.m_sketchimg) = obj.m_seglbs_per_sketchpts;
            end
        end
        
        function val = get.gdxy(obj)
            val = [];
            if ~isempty(obj.m_gdx)
                dx = obj.m_gdx(obj.m_sketchimg);
                dy = obj.m_gdy(obj.m_sketchimg);
                val = [dx(:) dy(:)]';
            end
        end
        
        function val = get.ptsketch(obj)
            val = [];
            if ~isempty(obj.m_sketchimg)
                [is,js] = find(obj.m_sketchimg);
                val = [is(:) js(:)]';
            end
        end
        
        function val = get.nameoflbs(obj)
            val = util_get_name_by_label(obj.m_dblbs);
        end
        
        %full score per label per cxpts, which takes context and matching into account
        [val,idxcandpivot_label2cxpts] = get_scoremap_label2cxpts(obj)
        
        val = get_candpivot_scores(obj)
        
        % get match information per label per point
        %         [scoremap,xymat] = get_matchinfo_by_pts(obj,labels,pts);
        
        % --- matching functions should be called in this order--
        % match sketch with database
        obj = match_to_database_by_fit(obj,database,varargin)
        
        % extract feature by database matching, must after match by fit
        obj = update_feature_with_matchdata(obj,database,varargin)
        
        % given the updated candpivots, update their context features
        obj = update_feature_with_candpivot(obj,database,varargin)
        
        % match to database by context, must after update feature
        obj = match_to_database_by_context(obj,database,varargin)
        
        % show 2d segments
        img = show_2dsegs(obj,varargin)
        
        % get ground truth labeling
        [lbmap,adjmat_label2pixel] = get_groundtruth_lbmap(obj,lbs)
        
        % get ground truth segmentation
        [lbmap,adjmat_seglb2pixel] = get_groundtruth_segmentation(obj)
        
        % get label per segmentation
        label_per_comp = get_groundtruth_label_per_component(obj)
        
        % set view and update database based on a user specified model
        obj = update_view_by_example_model(obj,comp,varargin)
        
        % update component coverage, to force each component to be fully
        % covered or not
        obj = update_fullcover(obj,database)
        
        % remove some candpivots
        obj = remove_candpivot(obj,idxcandpivot)
        
        % set candpivot by seglist
        obj = set_candpivot_by_seglist(obj,seglist,database,varargin)
        
        % take a bunch of sketch segments and make a subsketch
        obj = subsketch_by_seglbs(obj,seglbs,varargin)
        
        % make label map according to segment group
        lbmap = get_lbmap_from_segroup(obj,seglbs_per_group)
        
        % make label map from groundtruth segmentation
        [lbmap,lbmap_per_dblbs] = get_lbmap_from_segmentation(obj)
        
        % update seg2seg parallel grouping
        obj = update_parallel_grouping_seg2seg(obj,database)
        
        % segment linking information
        % val(i,j,:) = [indfrom,indto] means seglb i and seglb j connects
        % via indfrom on i and indto on j, indfrom/to is the image linear
        % coordinate
        % pts_seglb2seglb(i,j,:) = [ptsfrom;ptsto]
        [inds_seglb2seglb,pts_seglb2seglb] = get_seglb2seglb_link(obj);
        
        % stroke linking information
        % val(i,j,:) = [indfrom,indto] means stroke i and stroke j connects
        % via indfrom and indto. A stroke is a continuity group of seglbs
        % pts_stroke2stroke(i,j,:) = [ptsfrom;ptsto];
        % distmat(i,j) = the distance between points given by val(i,j,:)
        [inds_stroke2stroke,pts_stroke2stroke,distmat] = get_stroke2stroke_link(obj);
        
        % sequence stroke points
        pts_per_stroke = get_stroke_pts(obj)
    end
    
    methods(Static)
        [pts,idx] = select_sample_by_votepts_in_grid(sketchimg,gridsize,votepts,score_per_pts)
        scoremap = get_scoremap_by_pts( scoremaplist, scoremaplbs, pts,labels )
        [clusterlbs,idxbestcandpivots] = cluster_candpivots(candpivots,scores,adjmat_cp2cxpts,varargin)
        obj = init_with_sketchimg(sketchimg,varargin)
        obj = init_with_svg(svg,varargin)
        obj = init_with_vecimg(vecimg,imgsize,varargin)
        [imgsize,npixlong,vecimg] = imgsize_from_database(vecimg,database,varargin)
    end
    
end

