Mentions légales du service

Skip to content
Snippets Groups Projects
Viewer.m 7.11 KiB
classdef Viewer < handle
    %VIEWER Summary of this class goes here
    %   Detailed explanation goes here
    
    properties(SetObservable, AbortSet)
        data
        name = 'LightField'
        state = 'uv'
        range = 'class'
        curView = [1,1]
        curPos = [1,1]
    end
    
    properties
        h  = []
        im = []
        ax = []
        
        slices
    end
    
    properties(Dependent, Hidden)
        state_
        range_
    end
    
    properties(Hidden)
        classRange
        currentRange
        allRange
        validStates = {'xy','uv','ux','vy'};
        validRanges = {'class','current','all'};
    end
    
    methods
        function obj = Viewer(data,varargin)
            p = inputParser; p.StructExpand = true; p.KeepUnmatched = true;
            
            p.addRequired('data');
            p.addParameter('name' , obj.name  ,@ischar);
            p.addParameter('state', obj.state ,@(state) any(strcmp(state,obj.validStates)));
            p.addParameter('range', obj.range ,@(range) any(strcmpi(range,obj.validRanges)));
            
            p.parse(data,varargin{:});
            
            addlistener(obj,'data'  ,'PostSet' ,@(src,evnt) obj.updateSlices);
            addlistener(obj,'state' ,'PostSet' ,@(src,evnt) obj.updateSlices);
            
            res = p.Results;
            for fname = fieldnames(res)'
                fn = fname{:};
                obj.(fn) = res.(fn);
            end
            
            addlistener(obj,'curView' ,'PostSet' ,@(src,evnt) obj.updateFigure);
            addlistener(obj,'curPos'  ,'PostSet' ,@(src,evnt) obj.updateFigure);
            addlistener(obj,'range'   ,'PostSet' ,@(src,evnt) obj.updateFigure);
            addlistener(obj,'name'    ,'PostSet' ,@(src,evnt) obj.updateFigure);
            
            obj.setRanges();
            obj.view;
        end
        
        function state_ = get.state_(obj)
            nDims = ndims(obj.data);
            if nDims<=5
                switch obj.state
                    case 'xy'
                        state_ = [0 0 1 1,0];
                    case 'uv'
                        state_ = [1 1 0 0,0];
                    case 'ux'
                        state_ = [1 0 1 0,0];
                    case 'vy'
                        state_ = [0 1 0 1,0];
                    otherwise
                        error('Unexpected state');
                end
                state_ = state_(1:nDims);
            else
                error('Unexpected number of dimensions in input data');
            end
        end
        
        function range_ = get.range_(obj)
            
            switch lower(obj.range)
                case 'class'
                    range_ = obj.classRange;
                case 'current'
                    u=obj.curView(1); v=obj.curView(2);
                    range_ = obj.currentRange{u,v};
                case 'all'
                    range_ = obj.allRange;
                otherwise
            end
        end
        
        function view(obj)
            if ~obj.validFigure()
                obj.h  = figure;
                obj.ax = gca;
                
                cv = num2cell(obj.curView);
                
                obj.im = imagesc(obj.ax,obj.slices{cv{:}});
                colormap gray; axis image;
                
                obj.h.KeyPressFcn         = @obj.KeyPressCallback;
                obj.h.WindowButtonDownFcn = @obj.ButtonDownCallback;
                obj.h.WindowButtonUpFcn   = @obj.ButtonUpCallback;
                
                obj.updateFigure();
            end
        end
    end
    
    methods(Hidden)
        function setRanges(obj)
            obj.classRange = getrangefromclass(obj.data);
            
            cMin = cellfun(@(slice) min(slice(:)),obj.slices);
            cMax = cellfun(@(slice) max(slice(:)),obj.slices);
            
            cMin(isnan(cMin)) = obj.classRange(1);
            cMax(isnan(cMax)) = obj.classRange(2);
            
            minmax = cMin>=cMax;
            cMin(minmax) = cMax(minmax)-1;
            cMax(minmax) = cMin(minmax)+1;
            
            obj.currentRange = arrayfun(@(a,b) [a,b],cMin,cMax,'UniformOutput',false);
            
            obj.allRange = [min(cMin(:)) max(cMax(:))];
        end
        
        function updateSlices(obj)
            obj.slices = LF.split(obj.data,obj.state_);
        end
        
        function updateFigure(obj)
            if obj.validFigure()
                subind = logical(obj.state);
                subind(end+1:5) = false;

                coordStr = {'u','v','x','y','c'};
                coordStr(subind) = arrayfun(@num2str,obj.curView,'UniformOutput',false);
                coordStr(~subind) = deal({':'});
                coordStr = ['(' strjoin(coordStr,','),')'];
                
                title_{1} = [obj.name ' ' coordStr];
                title(title_);
                
                u=obj.curView(1); v=obj.curView(2);
                
                obj.im.CData = obj.slices{u,v};
                obj.im.AlphaData = double(any(~isnan(obj.im.CData),3));
                
                obj.ax.CLim = obj.range_;
            end
        end
        
        function KeyPressCallback(obj,~,evnt)
            curView_ = obj.curView;
            switch evnt.Key
                case    'uparrow'
                    curView_(1) = curView_(1)-1;
                case  'downarrow'
                    curView_(1) = curView_(1)+1;
                case  'leftarrow'
                    curView_(2) = curView_(2)-1;
                case 'rightarrow'
                    curView_(2) = curView_(2)+1;
                case 'space'
                    rangeInd = find(strcmp(obj.range,obj.validRanges));
                    rangeInd = mod(rangeInd,3)+1;
                    obj.range = obj.validRanges{rangeInd};
            end
            sz = size(obj.slices);
            obj.curView = min(sz,max([1,1],curView_));
        end
        
        function ButtonDownCallback(obj,~,~)
            refView = obj.curView;
            refPos = obj.ax.CurrentPoint(1,[2 1]);
            
            obj.h.WindowButtonMotionFcn = ...
                @(src,evnt) (obj.ButtonMotionCallback(src,evnt,refView,refPos));
        end
        
        function ButtonUpCallback(obj,~,~)
            obj.h.WindowButtonMotionFcn = '';
        end
        
        function ButtonMotionCallback(obj,~,~,refView,refPos)
            axSize = [diff(obj.ax.YLim) diff(obj.ax.XLim)];
            axPos = obj.ax.CurrentPoint(1,[2,1]);
            
            switch obj.h.SelectionType
                case 'normal'
                    slicesSize = size(obj.slices);
                    mv = 2.*(axPos - refPos)./axSize.*slicesSize;
                    curView_ = refView + round(mv);
                    obj.curView = min(slicesSize,max([1,1],curView_));
                case 'alt'
                    obj.curPos = flip(axPos);
            end
        end
        
        function bool = validFigure(obj)
            bool = ~isempty(obj.h) &&isvalid(obj.h) &&...
                ~isempty(obj.ax)&&isvalid(obj.ax)&&...
                ~isempty(obj.im)&&isvalid(obj.im);
        end
    end
end