diff --git a/@Viewer/Viewer.m b/@Viewer/Viewer.m index bb38bccc21670ca4084a92f9eeef113711304101..a23d7719e0813b633aff005c12a8b36fe5995265 100644 --- a/@Viewer/Viewer.m +++ b/@Viewer/Viewer.m @@ -2,34 +2,32 @@ classdef Viewer < handle %VIEWER Summary of this class goes here % Detailed explanation goes here - properties(SetObservable, AbortSet) + properties(SetObservable) data + dims = 'xy' name = 'LightField' - state = 'xy' range = 'class' - curView = [1,1] - curPos = [1,1] + cv = [1,1] + cmap = gray + slice + clim + title end properties h = [] im = [] ax = [] - - slices - end - - properties(Dependent, Hidden) - range_ end properties(Hidden) - classRange - currentRange - allRange - validStates = {'xy','uv','ux','vy','yx','vu','xu','yv'}; - validRanges = {'class','current','all'}; - statevec = logical([0,0,0,1,1]); + validDims = {'xy','uv','ux','vy','yx','vu','xu','yv'}; + validRanges = {'class','slice','data'}; + climClass + climData + gv + perm = [1,2,3,4,5] + maxView end methods @@ -37,9 +35,11 @@ classdef Viewer < handle p = inputParser; p.StructExpand = true; p.KeepUnmatched = true; p.addRequired('data'); + p.addParameter('dims' , obj.dims ,@(dims) any(strcmpi(dims,obj.validDims))); p.addParameter('name' , obj.name ,@ischar); - p.addParameter('state', obj.state ,@(state) any(strcmpi(state,obj.validStates))); p.addParameter('range', obj.range ,@(range) any(strcmpi(range,obj.validRanges))); + p.addParameter('cv' , obj.cv ,@isnumeric); + p.addParameter('cmap' , obj.cmap ,@isnumeric); p.parse(data,varargin{:}); @@ -49,29 +49,24 @@ classdef Viewer < handle obj.(fn) = res.(fn); end - addlistener(obj, 'data' , 'PostSet', @(src,evnt) obj.updateSlices); - addlistener(obj, 'state' , 'PostSet', @(src,evnt) obj.updateSlices); - 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.updateData(); + obj.updateDims(); - obj.updateSlices(); - obj.setRanges(); - obj.view; - 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 + obj.updateSlice(); + obj.updateCLim(); + obj.updateTitle(); + + addlistener(obj, 'data' , 'PostSet', @(src,evnt) obj.updateData); + addlistener(obj, 'dims' , 'PostSet', @(src,evnt) obj.updateDims); + addlistener(obj, 'name' , 'PostSet', @(src,evnt) obj.updateName); + addlistener(obj, 'range', 'PostSet', @(src,evnt) obj.updateRange); + addlistener(obj, 'cv' , 'PostSet', @(src,evnt) obj.updateCV); + addlistener(obj, 'cmap' , 'PostSet', @(src,evnt) obj.updateCMap); + addlistener(obj, 'slice', 'PostSet', @(src,evnt) obj.updateSlice); + addlistener(obj, 'title', 'PostSet', @(src,evnt) obj.updateTitle); + addlistener(obj, 'clim' , 'PostSet', @(src,evnt) obj.updateCLim); + + obj.view(); end function view(obj) @@ -79,107 +74,165 @@ classdef Viewer < handle obj.h = figure; obj.ax = gca; - cv = num2cell(obj.curView); - - obj.im = imagesc(obj.ax,obj.slices{cv{:}}); - colormap gray; axis image; + obj.im = imagesc(obj.ax,obj.slice); + obj.ax.Colormap = obj.cmap; + axis(obj.ax,'image'); obj.h.KeyPressFcn = @obj.KeyPressCallback; obj.h.WindowButtonDownFcn = @obj.ButtonDownCallback; obj.h.WindowButtonUpFcn = @obj.ButtonUpCallback; - - obj.updateFigure(); end + + obj.updateFigure(); 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; + function updateData(obj) + if islogical(obj.data) + data_ = obj.data; + data_ = uint8(data_); + obj.data = []; + obj.data = data_; + end - obj.currentRange = arrayfun(@(a,b) [a,b],cMin,cMax,'UniformOutput',false); + obj.gv = arrayfun(@(x) 1:x,size(obj.data),'UniformOutput',false); + obj.climClass = getrangefromclass(obj.data); + obj.climData = [min(obj.data(:)) max(obj.data(:))]; - obj.allRange = [min(cMin(:)) max(cMax(:))]; + obj.updateCV(); + end + + function updateDims(obj) + obj.updatePerm(); + obj.updateCV(); end - function updateSlices(obj) - switch lower(obj.state) + function updatePerm(obj) + switch lower(obj.dims) case 'xu' - perm = [1,4,3,2,5]; + obj.perm = [1,4,3,2,5]; case 'ux' - perm = [4,1,3,2,5]; + obj.perm = [4,1,3,2,5]; case 'yv' - perm = [2,5,3,1,4]; + obj.perm = [2,5,3,1,4]; case 'vy' - perm = [5,2,3,1,4]; + obj.perm = [5,2,3,1,4]; case 'xy' - perm = [1,2,3,4,5]; + obj.perm = [1,2,3,4,5]; case 'yx' - perm = [2,1,3,4,5]; + obj.perm = [2,1,3,4,5]; case 'uv' - perm = [4,5,3,1,2]; + obj.perm = [4,5,3,1,2]; case 'vu' - perm = [5,4,3,1,2]; + obj.perm = [5,4,3,1,2]; end + end + + function updateName(obj) + obj.updateTitle(); + end + + function updateRange(obj) + obj.updateCLim(); + end + + function updateCV(obj) + obj.maxView = arrayfun(@(x) size(obj.data,x),obj.perm(4:5)); + obj.cv = min(obj.maxView,max([1,1],obj.cv)); - obj.slices = LF.toSlices(permute(obj.data,perm)); - obj.statevec = false(1,5); - obj.statevec(perm(4:5)) = true; + obj.updateTitle(); + obj.updateSlice(); end - function updateFigure(obj) + function updateSlice(obj) + gv_ = obj.gv; + ccv = num2cell(obj.cv); + [gv_{obj.perm(4:5)}] = deal(ccv{:}); + + obj.slice = permute(obj.data(gv_{:}),obj.perm); + if obj.validFigure() - - coordStr = {'x','y','c','u','v'}; - coordStr(obj.statevec) = arrayfun(@num2str,obj.curView,'UniformOutput',false); - coordStr(~obj.statevec) = deal({':'}); - coordStr = ['(' strjoin(coordStr,','),')']; - - title_{1} = [obj.name ' ' coordStr]; - title(title_); - - cv = num2cell(obj.curView); - - obj.im.CData = obj.slices{cv{:}}; + obj.im.CData = obj.slice; obj.im.AlphaData = double(any(~isnan(obj.im.CData),3)); - - obj.ax.CLim = obj.range_; end end + function updateCLim(obj) + switch lower(obj.range) + case 'class' + obj.clim = obj.climClass; + case 'slice' + obj.clim = [min(obj.slice(:)) max(obj.slice(:))]; + case 'data' + obj.clim = obj.climData; + end + + if diff(obj.clim)<=0 + obj.clim(2) = obj.clim(1) + diff(getrangefromclass(obj.clim))/2; + end + + if obj.validFigure() + obj.ax.CLim = obj.clim; + end + end + + function updateTitle(obj) + coordVec = false(1,5); + coordVec(obj.perm(4:5)) = true; + + coordStr = {'x','y','c','u','v'}; + coordStr(coordVec) = arrayfun(@num2str,obj.cv,'UniformOutput',false); + coordStr(~coordVec) = deal({':'}); + coordStr = ['(' strjoin(coordStr,','),')']; + + obj.title = [obj.name,' ',coordStr]; + + if obj.validFigure() + obj.ax.Title.String = obj.title; + end + end + + function updateCMap(obj) + if obj.validFigure() + obj.ax.Colormap = obj.cmap; + end + end + + function updateFigure(obj) + if ~obj.validFigure() + return + end + + obj.im.CData = obj.slice; + obj.im.AlphaData = double(any(~isnan(obj.im.CData),3)); + obj.ax.CLim = obj.clim; + obj.ax.Title.String = obj.title; + obj.ax.Colormap = obj.cmap; + end + function KeyPressCallback(obj,~,evnt) - curView_ = obj.curView; + cv_ = obj.cv; switch evnt.Key case 'uparrow' - curView_(1) = curView_(1)-1; + cv_(1) = cv_(1)-1; case 'downarrow' - curView_(1) = curView_(1)+1; + cv_(1) = cv_(1)+1; case 'leftarrow' - curView_(2) = curView_(2)-1; + cv_(2) = cv_(2)-1; case 'rightarrow' - curView_(2) = curView_(2)+1; + cv_(2) = cv_(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_)); + obj.cv = min(obj.maxView,max([1,1],cv_)); end function ButtonDownCallback(obj,~,~) - refView = obj.curView; + refView = obj.cv; refPos = obj.ax.CurrentPoint(1,[2 1]); obj.h.WindowButtonMotionFcn = ... @@ -191,24 +244,20 @@ classdef Viewer < handle end function ButtonMotionCallback(obj,~,~,refView,refPos) - axSize = [diff(obj.ax.YLim) diff(obj.ax.XLim)]; + 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_)); + mv = 2.*(axPos - refPos)./axSize.*obj.maxView; + cv_ = refView + round(mv); + obj.cv = min(obj.maxView,max([1,1],cv_)); 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); + bool = ~isempty(obj.im) && isvalid(obj.im); end end end \ No newline at end of file