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