diff --git a/src/MATLAB/+Editor/CreateMitosisAction.m b/src/MATLAB/+Editor/CreateMitosisAction.m index c4ca7dbbac402e82224872102dff652629ad7ebd..c16ce02198cf7b4cc7e6b2f481e2beadcaf92e63 100644 --- a/src/MATLAB/+Editor/CreateMitosisAction.m +++ b/src/MATLAB/+Editor/CreateMitosisAction.m @@ -3,83 +3,42 @@ % % Create user identified mitosis events and add to current tree. -function historyAction = CreateMitosisAction(trackID, dirFlag, treeID, time, linePoints) - global CellFamilies CellTracks HashedCells +function historyAction = CreateMitosisAction(trackID, dirFlag, time, linePoints) + global CellTracks if ( time < 2 ) error('Mitosis event cannot be defined in the first frame'); end - treeTracks = [CellFamilies(treeID).tracks]; - - bInTracks = Helper.CheckInTracks(time, treeTracks, 0, 0); - checkTracks = treeTracks(bInTracks); - if ( isempty(checkTracks) ) - error('No valid tracks to add a mitosis onto'); - end - - startTimes = [CellTracks(checkTracks).startTime]; - [minTime minIdx] = min(startTimes); - - forceParents = []; - % If a mitosis is specified RIGHT after another one, we have a problem - % so force old mitosis children into the list - if ( minTime == time-1 ) - forceParents = arrayfun(@(x)(CellTracks(x).hulls(1)), checkTracks); - end - + treeID = CellTracks(trackID).familyID; linePoints = clipToImage(linePoints); - % Find or create hulls to define mitosis event childHulls = Segmentation.MitosisEditor.FindChildrenHulls(linePoints, time); - parentHull = Segmentation.MitosisEditor.FindParentHull(childHulls, linePoints, time-1, forceParents); - -% costMatrix = Tracker.GetCostMatrix(); - -% bLeafTrack = arrayfun(@(x)(isempty(x.childrenTracks)), CellTracks(treeTracks)); -% leafTracks = treeTracks(bLeafTrack); - - Helper.SetTreeLocked(treeID, 0); + parentHull = Segmentation.MitosisEditor.FindParentHull(childHulls, linePoints, time-1); - % NOTE: This just makes the tree as balanced as possible, it is probably not correct - balancedTrack = checkTracks(minIdx); - parentTrack = Hulls.GetTrackID(parentHull); + chkDropHull = Helper.GetNearestTrackHull(trackID, time+1,+1); - % TODO: Don't change things up if parentTrack is already complete and on - % the correct family - if ( CellTracks(parentTrack).familyID == treeID ) - balancedTrack = parentTrack; + parentTrackID = Hulls.GetTrackID(parentHull); + if ( parentTrackID ~= trackID ) + parentTrackID = Tracks.TearoffHull(parentHull); + + Tracks.ChangeTrackID(parentTrackID, trackID, time-1); end - if ( ~isempty(trackID) && dirFlag > 0 ) - balancedTrack = trackID; - else - error('Adding mitosis event above edit is currently unsupported!'); + chkTrackID = Hulls.GetTrackID(childHulls(1)); + if ( chkTrackID ~= trackID ) + chkTrackID = Tracks.TearoffHull(childHulls(1)); + Tracks.ChangeLabel(chkTrackID, trackID, time); end - if ( balancedTrack ~= parentTrack ) - attemptLockedChangeLabel(parentTrack, balancedTrack, time-1); - end + newTrackID = Tracks.TearoffHull(childHulls(2)); + Families.AddMitosis(newTrackID, trackID, time); - childTrack = Hulls.GetTrackID(childHulls(1)); - if ( balancedTrack ~= childTrack ) - attemptLockedChangeLabel(childTrack, balancedTrack, time); - end - - childTrack = Hulls.GetTrackID(childHulls(2)); - if ( Helper.CheckTreeLocked(childTrack) ) - error('Not yet implemented: Locked "addMitosis"'); - else - Families.AddMitosis(childTrack, balancedTrack, time); - end - - % TODO: Make this respect the endTime from start of state - if ( time < length(HashedCells) ) - for i=1:2 - childTrack = Hulls.GetTrackID(childHulls(i)); - Helper.DropSubtree(childTrack); - end + if ( chkDropHull > 0 ) + leftChildTrackID = Hulls.GetTrackID(childHulls(1)); + fixupChildTrackID = Hulls.GetTrackID(chkDropHull); + Tracks.ChangeLabel(fixupChildTrackID, leftChildTrackID, time+1); end Helper.SetTreeLocked(treeID, 1); @@ -87,14 +46,6 @@ function historyAction = CreateMitosisAction(trackID, dirFlag, treeID, time, lin historyAction = 'Push'; end -function attemptLockedChangeLabel(changeTrack, desiredTrack, time) - if ( Helper.CheckTreeLocked(changeTrack) ) - Tracks.LockedChangeLabel(changeTrack, desiredTrack, time); - else - Tracks.ChangeLabel(changeTrack, desiredTrack, time); - end -end - function newPoints = clipToImage(linePoints) global CONSTANTS diff --git a/src/MATLAB/+Editor/MitosisEditInitializeAction.m b/src/MATLAB/+Editor/MitosisEditInitializeAction.m index bf3f3596a1bafa27b12fda57cf7767278ed2dbdf..c139554d0132b4fdd339a4d965ad4cd7d7ff876a 100644 --- a/src/MATLAB/+Editor/MitosisEditInitializeAction.m +++ b/src/MATLAB/+Editor/MitosisEditInitializeAction.m @@ -14,5 +14,5 @@ function historyAction = MitosisEditInitializeAction(treeID, endTime) CellFamilies(treeID).bLocked = 1; - historyAction = 'Push'; + historyAction = ''; end diff --git a/src/MATLAB/+Editor/MitosisHullPhenotypeAction.m b/src/MATLAB/+Editor/MitosisHullPhenotypeAction.m new file mode 100644 index 0000000000000000000000000000000000000000..8aea85b38b6bf6a7c4b51d3c9904c3fdc1070113 --- /dev/null +++ b/src/MATLAB/+Editor/MitosisHullPhenotypeAction.m @@ -0,0 +1,20 @@ +% [historyAction newTrack] = MitosisHullPhenotypeAction() +% Edit Action: + +function [historyAction hullID] = MitosisHullPhenotypeAction(clickPoint, time, trackID) + + hullID = Hulls.FindHull(time, clickPoint); + if ( hullID <= 0 ) + newTrackID = Segmentation.AddNewSegmentHull(clickPoint, time); + hullID = Tracks.GetHullID(time, newTrackID); + else + newTrackID = Hulls.GetTrackID(hullID); + end + + if ( newTrackID ~= trackID ) + newTrackID = Tracks.TearoffHull(hullID); + Tracks.ChangeLabel(newTrackID, trackID, time); + end + + historyAction = ''; +end diff --git a/src/MATLAB/+Tracks/TearoffHull.m b/src/MATLAB/+Tracks/TearoffHull.m new file mode 100644 index 0000000000000000000000000000000000000000..b14dc68d0c532a31aeafa2428593728c2e0225eb --- /dev/null +++ b/src/MATLAB/+Tracks/TearoffHull.m @@ -0,0 +1,38 @@ +function newTrackID = TearoffHull(hullID) + global CellHulls CellTracks + + time = CellHulls(hullID).time; + trackID = Hulls.GetTrackID(hullID); + + if ( length(CellTracks(trackID).hulls) == 1 ) + newTrackID = trackID; + return; + end + + bLeafTrack = isempty(CellTracks(trackID).childrenTracks); + + bStartHull = (CellTracks(trackID).startTime == time); + bEndHull = (CellTracks(trackID).endTime == time); + + droppedTracks = Families.RemoveFromTree(trackID, time); + + newTrackID = Hulls.GetTrackID(hullID); + if ( bEndHull ) + % Tearoff both children since can't reconstruct mitosis without + % this hull + if ( ~bLeafTrack ) + Families.RemoveFromTreePrune(CellTracks(newTrackID).childrenTracks(1)); + end + return; + end + + % Tearoff rest of subtree if this is root or the start of a track + % (can't reattach mitosis events without a new hull) + if ( bStartHull ) + Families.RemoveFromTreePrune(newTrackID,time+1); + return; + end + + % Reattach the rest of the track + Tracks.ChangeLabel(newTrackID, trackID, time+1); +end diff --git a/src/MATLAB/+UI/CreateContextMenuCells.m b/src/MATLAB/+UI/CreateContextMenuCells.m index 53d2b9d4ecc490c518cfb7ab17bf17f6975ded87..d9150b8dec82f2952b114b0b83524356917a3b01 100644 --- a/src/MATLAB/+UI/CreateContextMenuCells.m +++ b/src/MATLAB/+UI/CreateContextMenuCells.m @@ -81,10 +81,6 @@ uimenu(Figures.cells.contextMenuHandle,... 'Label', 'Remove Cell (this frame)',... 'CallBack', @removeHull); -uimenu(Figures.cells.contextMenuHandle,... - 'Label', 'Delete Track',... - 'CallBack', @removeTrackPrevious); - uimenu(Figures.cells.contextMenuHandle,... 'Label', 'Remove From Tree',... 'CallBack', @removeFromTree); diff --git a/src/MATLAB/+UI/DrawCells.m b/src/MATLAB/+UI/DrawCells.m index 593360e970aaf80fd5a46350d4541f7f255bf23f..d4b6f8bd7dde924f3587c3cfecc475efc3949163 100644 --- a/src/MATLAB/+UI/DrawCells.m +++ b/src/MATLAB/+UI/DrawCells.m @@ -102,13 +102,13 @@ if(strcmp(get(Figures.cells.menuHandles.imageMenu, 'Checked'),'off')) set(im,'Visible','off'); end -bDrawLabels = isempty(Figures.tree.movingMitosis); +bDrawLabels = true; drawHullFilter = []; if ( strcmpi(Figures.cells.editMode, 'mitosis') ) % Filter so we only draw family "mitosis" hulls when editing bDrawLabels = 0; - drawHullFilter = arrayfun(@(x)(CellTracks(x).hulls(1)),... + drawHullFilter = arrayfun(@(x)(CellTracks(x).hulls(CellTracks(x).hulls~=0)),... CellFamilies(Figures.tree.familyID).tracks, 'UniformOutput',0); drawHullFilter = [drawHullFilter{:}]; end @@ -131,14 +131,6 @@ if(strcmp(get(Figures.cells.menuHandles.labelsMenu, 'Checked'),'on')) curHullID = HashedCells{Figures.time}(i).hullID; curTrackID = HashedCells{Figures.time}(i).trackID; - %if dragging a mitosis, only show the siblings: Draws faster - %and makes it easier to follow what is changing - if(Figures.tree.movingMitosis) - if(Figures.tree.movingMitosis ~= curTrackID) - continue; - end - end - if ( ~isempty(drawHullFilter) && ~any(curHullID == drawHullFilter ) ) continue; end @@ -262,13 +254,7 @@ end Figures.cells.axesHandle = curAx; -if(~isempty(Figures.cells.PostDrawHookOnce)) - for i=1:length(Figures.cells.PostDrawHookOnce) - hook = Figures.cells.PostDrawHookOnce{i}; - hook(curAx); - end - Figures.cells.PostDrawHookOnce = {}; -end + drawnow(); end diff --git a/src/MATLAB/+UI/DrawTree.m b/src/MATLAB/+UI/DrawTree.m index af969d2a962c3a8dfcc13d866368e34e91268a6e..d51b5ab365ce8d2d47ceb5f5a780ffd047770804 100644 --- a/src/MATLAB/+UI/DrawTree.m +++ b/src/MATLAB/+UI/DrawTree.m @@ -351,6 +351,7 @@ function drawCellLabel(curAx, trackID, xVal, bDrawLabels) 'FontSize',fontSize,... 'color',textColor,... 'UserData',trackID,... + 'ButtonDownFcn',@(src,evnt)(UI.FigureTreeDown(src,evnt,trackID)),... 'uicontextmenu',Figures.tree.contextMenuHandle); % Draw a dead cell marker @@ -362,6 +363,7 @@ function drawCellLabel(curAx, trackID, xVal, bDrawLabels) 'MarkerEdgeColor','r',... 'MarkerSize',circleSize,... 'UserData',trackID,... + 'ButtonDownFcn',@(src,evnt)(UI.FigureTreeDown(src,evnt,trackID)),... 'uicontextmenu',Figures.tree.contextMenuHandle); UI.DrawPool.SetDrawOrder(Figures.tree.axesHandle, [hMarker hLabel]); @@ -379,6 +381,7 @@ function drawCellLabel(curAx, trackID, xVal, bDrawLabels) 'MarkerEdgeColor','w',... 'MarkerSize',phenoScale*circleSize,... 'UserData',trackID,... + 'ButtonDownFcn',@(src,evnt)(UI.FigureTreeDown(src,evnt,trackID)),... 'uicontextmenu',Figures.tree.contextMenuHandle); end @@ -390,6 +393,7 @@ function drawCellLabel(curAx, trackID, xVal, bDrawLabels) 'MarkerEdgeColor',CellTracks(trackID).color.background,... 'MarkerSize',circleSize,... 'UserData',trackID,... + 'ButtonDownFcn',@(src,evnt)(UI.FigureTreeDown(src,evnt,trackID)),... 'uicontextmenu',Figures.tree.contextMenuHandle); @@ -406,6 +410,7 @@ function drawCellLabel(curAx, trackID, xVal, bDrawLabels) 'MarkerEdgeColor','k',... 'MarkerSize',circleSize,... 'UserData',trackID,... + 'ButtonDownFcn',@(src,evnt)(UI.FigureTreeDown(src,evnt,trackID)),... 'uicontextmenu',Figures.tree.contextMenuHandle); end diff --git a/src/MATLAB/+UI/FigureCellUp.m b/src/MATLAB/+UI/FigureCellUp.m index 7c1049f1891b4622d1933c0f3750fbeeda33c2ff..bdffa44d3b4ffc8fca6475f970ce95070255a0b8 100644 --- a/src/MATLAB/+UI/FigureCellUp.m +++ b/src/MATLAB/+UI/FigureCellUp.m @@ -28,7 +28,7 @@ if ( strcmpi(Figures.cells.editMode, 'mitosis') ) UI.DrawCells(); else curFamID = Figures.tree.familyID; - drawHullFilter = arrayfun(@(x)(CellTracks(x).hulls(1)), CellFamilies(curFamID).tracks, 'UniformOutput',0); + drawHullFilter = arrayfun(@(x)(CellTracks(x).hulls(CellTracks(x).hulls~=0)), CellFamilies(curFamID).tracks, 'UniformOutput',0); drawHullFilter = [drawHullFilter{:}]; bCurHulls = ([CellHulls(drawHullFilter).time] == Figures.time); @@ -103,6 +103,11 @@ function addMitosisEvent(treeID, time, dragCoords) return end + if ( isempty(MitosisEditStruct) || ~isfield(MitosisEditStruct,'selectedTrackID') || isempty(MitosisEditStruct.selectedTrackID) ) + msgbox('No cells selected for mitosis identification','No Cell Selected','warn'); + return; + end + treeTracks = [CellFamilies(treeID).tracks]; bInTracks = Helper.CheckInTracks(time, treeTracks, 0, 0); @@ -124,7 +129,7 @@ function addMitosisEvent(treeID, time, dragCoords) % end dirFlag = UI.MitosisGetSelectedDirTo(time); - bErr = Editor.ReplayableEditAction(@Editor.CreateMitosisAction, MitosisEditStruct.selectedTrackID, dirFlag, treeID, time, (dragCoords.')); + bErr = Editor.ReplayableEditAction(@Editor.CreateMitosisAction, MitosisEditStruct.selectedTrackID, dirFlag, time, (dragCoords.')); if ( bErr ) return; end diff --git a/src/MATLAB/+UI/FigureKeyPress.m b/src/MATLAB/+UI/FigureKeyPress.m new file mode 100644 index 0000000000000000000000000000000000000000..845931b2a8f070732664394c2b702a5e9bd44cee --- /dev/null +++ b/src/MATLAB/+UI/FigureKeyPress.m @@ -0,0 +1,152 @@ +function FigureKeyPress(src,evnt) + global Figures ResegState + + if strcmp(evnt.Key,'downarrow') || strcmp(evnt.Key,'rightarrow') + if ( Figures.controlDown ) + time = findMitosis(+1, Figures.time, Figures.tree.familyID); + UI.TimeChange(time); + else + time = Figures.time + 1; + UI.TimeChange(time); + end + elseif strcmp(evnt.Key,'uparrow') || strcmp(evnt.Key,'leftarrow') + if ( Figures.controlDown ) + time = findMitosis(-1, Figures.time, Figures.tree.familyID); + UI.TimeChange(time); + else + time = Figures.time - 1; + UI.TimeChange(time); + end + elseif strcmp(evnt.Key,'pagedown') + time = Figures.time + 5; + UI.TimeChange(time); + elseif strcmp(evnt.Key,'pageup') + time = Figures.time - 5; + UI.TimeChange(time); + elseif strcmp(evnt.Key,'space') + if ( ~isempty(ResegState) ) + % Toggle reseg playing + buttonHandles = get(ResegState.toolbar, 'UserData'); + toggleFunc = get(buttonHandles(2), 'ClickedCallback'); + toggleFunc(buttonHandles(2), []); + else + % standard movie playing + UI.TogglePlay(src,evnt); + end + elseif strcmp(evnt.Key,'period') + if ( ~isempty(ResegState) ) + % Reseg Forward 1 Frame + buttonHandles = get(ResegState.toolbar, 'UserData'); + toggleFunc = get(buttonHandles(3), 'ClickedCallback'); + toggleFunc(buttonHandles(3), []); + else + % step forward one frame + time = Figures.time + 1; + UI.TimeChange(time); + end + elseif strcmp(evnt.Key,'comma') + if ( ~isempty(ResegState) ) + % Reseg Backward 1 Frame + buttonHandles = get(ResegState.toolbar, 'UserData'); + toggleFunc = get(buttonHandles(1), 'ClickedCallback'); + toggleFunc(buttonHandles(1), []); + else + % step backward one frame + time = Figures.time - 1; + UI.TimeChange(time); + end + elseif ( strcmp(evnt.Key,'control') ) + if(~Figures.controlDown) + %prevent this from getting reset when moving the mouse + Figures.controlDown = true; + end + elseif ( strcmp(evnt.Key,'delete') || strcmp(evnt.Key,'backspace') ) + deleteSelectedCells(); + elseif (strcmp(evnt.Key,'f12')) + Figures.cells.showInterior = ~Figures.cells.showInterior; + UI.DrawCells(); + UI.DrawTree(Figures.tree.familyID); + elseif ( strcmp(evnt.Key,'return') ) + mergeSelectedCells(); + end +end + +function mitTime = findMitosis(dirFlag, time, familyID) + global CellFamilies CellTracks HashedCells + + mitTime = 1; + if ( dirFlag > 0 ) + mitTime = length(HashedCells); + end + + if ( isempty(familyID) ) + return; + end + + famTracks = CellFamilies(familyID).tracks; + if ( isempty(famTracks) ) + return; + end + + % Append start and end of family and movie to mitosis times list. + chkTimes = unique([1 CellTracks(famTracks).startTime CellFamilies(familyID).endTime length(HashedCells)]); + + if ( dirFlag < 0 ) + idx = find(chkTimes < time, 1, 'last'); + else + idx = find(chkTimes > time, 1, 'first'); + end + + if ( isempty(idx) ) + return; + end + + mitTime = chkTimes(idx); +end + +function deleteSelectedCells() + global Figures CellFamilies + + bErr = Editor.ReplayableEditAction(@Editor.DeleteCells, Figures.cells.selectedHulls); + if ( bErr ) + return; + end + + UI.ClearCellSelection(); + Error.LogAction(['Removed selected cells [' num2str(Figures.cells.selectedHulls) ']'],Figures.cells.selectedHulls); + + %if the whole family disappears with this change, pick a diffrent family to display + if(isempty(CellFamilies(Figures.tree.familyID).tracks)) + for i=1:length(CellFamilies) + if(~isempty(CellFamilies(i).tracks)) + Figures.tree.familyID = i; + break + end + end + end + + UI.DrawTree(Figures.tree.familyID); + UI.DrawCells(); +end + +function mergeSelectedCells() + global Figures + + [bErr deletedCells replaceCell] = Editor.ReplayableEditAction(@Editor.MergeCells, Figures.cells.selectedHulls); + if ( bErr ) + return; + end + + if ( isempty(replaceCell) ) + msgbox(['Unable to merge [' num2str(Figures.cells.selectedHulls) '] in this frame'],'Unable to Merge','help','modal'); + return; + end + + + UI.ClearCellSelection(); + Error.LogAction('Merged cells', [deletedCells replaceCell], replaceCell); + + UI.DrawTree(Figures.tree.familyID); + UI.DrawCells(); +end + diff --git a/src/MATLAB/+UI/FigureScroll.m b/src/MATLAB/+UI/FigureScroll.m new file mode 100644 index 0000000000000000000000000000000000000000..cdd78c75af4aab69210747b8ab50a17e11e75abf --- /dev/null +++ b/src/MATLAB/+UI/FigureScroll.m @@ -0,0 +1,7 @@ +function FigureScroll(src,evnt) + global Figures + + time = Figures.time + evnt.VerticalScrollCount; + + UI.TimeChange(time); +end diff --git a/src/MATLAB/+UI/FigureTreeDown.m b/src/MATLAB/+UI/FigureTreeDown.m new file mode 100644 index 0000000000000000000000000000000000000000..3e82161a359fa1fdb18676b57c016f02f0a06562 --- /dev/null +++ b/src/MATLAB/+UI/FigureTreeDown.m @@ -0,0 +1,20 @@ +function FigureTreeDown(src,evnt, trackID) + global Figures CellTracks + + if ( ~exist('trackID','var') ) + trackID = []; + end + + if ( ~isempty(trackID) && strcmpi(Figures.cells.editMode, 'mitosis') ) + time = CellTracks(trackID).startTime; + + UI.MitosisSelectTrackingCell(trackID, time); + return; + end + + if(strcmp(get(Figures.tree.handle,'SelectionType'),'normal')) + set(Figures.tree.handle, 'WindowButtonMotionFcn',@UI.FigureTreeDrag); + + UI.MoveLine(); + end +end diff --git a/src/MATLAB/+UI/FigureTreeDrag.m b/src/MATLAB/+UI/FigureTreeDrag.m new file mode 100644 index 0000000000000000000000000000000000000000..3c1a7c383863bcc628282c52a84dafd0efa61947 --- /dev/null +++ b/src/MATLAB/+UI/FigureTreeDrag.m @@ -0,0 +1,5 @@ +function FigureTreeDrag(src,evnt) + + UI.MoveLine(); + UI.DrawCells(); +end diff --git a/src/MATLAB/+UI/FigureTreeUp.m b/src/MATLAB/+UI/FigureTreeUp.m new file mode 100644 index 0000000000000000000000000000000000000000..8b67a68445e4f3342679dd63bf5f616cea40ffa8 --- /dev/null +++ b/src/MATLAB/+UI/FigureTreeUp.m @@ -0,0 +1,9 @@ +function FigureTreeUp(src,evnt) + global Figures + + set(Figures.tree.handle, 'WindowButtonMotionFcn',''); + + if(strcmp(get(Figures.tree.handle,'SelectionType'),'normal')) + UI.TimeChange(Figures.time); + end +end diff --git a/src/MATLAB/+UI/InitializeFigures.m b/src/MATLAB/+UI/InitializeFigures.m index d16d45ddf46de85882e1959529372e5cb5c185e3..fb52d444eba250b1d23a1e996f570cea1f4aeef7 100644 --- a/src/MATLAB/+UI/InitializeFigures.m +++ b/src/MATLAB/+UI/InitializeFigures.m @@ -47,7 +47,6 @@ Figures.tree.handle = figure(); Figures.cells.selectedHulls = []; Figures.controlDown = false; %control key is currently down? for selecting cells and fine adjustment -Figures.cells.PostDrawHookOnce = {}; %list of functions to call post DrawCells. Cleared on every draw Figures.downHullID = -1; Figures.downClickPoint = [0 0]; @@ -56,14 +55,14 @@ whitebg(Figures.cells.handle,'k'); whitebg(Figures.tree.handle,'w'); Figures.advanceTimerHandle = timer(... - 'TimerFcn', @play,... + 'TimerFcn', @UI.Play,... 'Period', .05,... 'ExecutionMode', 'fixedSpacing',... 'BusyMode', 'drop'); set(Figures.cells.handle,... - 'WindowScrollWheelFcn', @figureScroll,... - 'KeyPressFcn', @figureKeyPress,... + 'WindowScrollWheelFcn', @UI.FigureScroll,... + 'KeyPressFcn', @UI.FigureKeyPress,... 'Menu', 'none',... 'ToolBar', 'figure',... 'BusyAction', 'cancel',... @@ -74,8 +73,7 @@ set(Figures.cells.handle,... 'Tag', 'cells',... 'ResizeFcn', @UI.UpdateSegmentationEditsMenu); -addlistener(Figures.cells.handle, 'WindowKeyRelease', @figureKeyRelease); - Figures.tree.movingMitosis = []; +addlistener(Figures.cells.handle, 'WindowKeyRelease', @UI.KeyStateRelease); Figures.cells.showInterior = false; Figures.cells.editMode = 'normal'; @@ -94,6 +92,7 @@ Figures.cells.cellCountLabel = uicontrol(Figures.cells.handle,... hPan = pan(Figures.cells.handle); set(hPan,'ActionPostCallback',@forceRedrawCells); + hZoom = zoom(Figures.cells.handle); set(hZoom,'ActionPostCallback',@forceRedrawCells); @@ -101,10 +100,10 @@ UI.CreateMenuBar(Figures.cells.handle); UI.CreateContextMenuCells(); set(Figures.tree.handle,... - 'WindowButtonDownFcn', @figureTreeDown,... - 'WindowButtonUpFcn', @figureTreeUp,... - 'WindowScrollWheelFcn', @figureScroll,... - 'KeyPressFcn', @figureKeyPress,... + 'WindowButtonDownFcn', @UI.FigureTreeDown,... + 'WindowButtonUpFcn', @UI.FigureTreeUp,... + 'WindowScrollWheelFcn', @UI.FigureScroll,... + 'KeyPressFcn', @UI.FigureKeyPress,... 'CloseRequestFcn', @UI.CloseFigure,... 'Menu', 'none',... 'ToolBar', 'figure',... @@ -114,12 +113,8 @@ set(Figures.tree.handle,... 'Name', [CONSTANTS.datasetName ' Lineage'],... 'Tag', 'tree'); - addlistener(Figures.tree.handle, 'WindowKeyRelease', @figureKeyRelease); - -%WindowButtonMotionFcn callbacks cause 'CurrentPointer' to be updated -%which is needed for dragging -set(Figures.tree.handle, 'WindowButtonMotionFcn',@(src,evt)(src)); -Figures.tree.dragging = []; +addlistener(Figures.tree.handle, 'WindowKeyRelease', @UI.KeyStateRelease); + Figures.tree.trackMap = []; Figures.tree.trackingLine = []; Figures.tree.trackingLabel = []; @@ -166,288 +161,6 @@ end %% Callback Functions -function figureScroll(src,evnt) -global Figures -time = Figures.time + evnt.VerticalScrollCount; -UI.TimeChange(time); -end - -function play(src,event) -global Figures HashedCells -time = Figures.time + 1; -if(time == length(HashedCells)) - time = 1; -end -UI.TimeChange(time); -end - -function figureKeyPress(src,evnt) -global Figures CellFamilies ResegState - -if strcmp(evnt.Key,'downarrow') || strcmp(evnt.Key,'rightarrow') - time = Figures.time + 1; - UI.TimeChange(time); -elseif strcmp(evnt.Key,'uparrow') || strcmp(evnt.Key,'leftarrow') - time = Figures.time - 1; - UI.TimeChange(time); -elseif strcmp(evnt.Key,'pagedown') - time = Figures.time + 5; - UI.TimeChange(time); -elseif strcmp(evnt.Key,'pageup') - time = Figures.time - 5; - UI.TimeChange(time); -elseif strcmp(evnt.Key,'space') - if ( ~isempty(ResegState) ) - % Toggle reseg playing - buttonHandles = get(ResegState.toolbar, 'UserData'); - toggleFunc = get(buttonHandles(2), 'ClickedCallback'); - toggleFunc(buttonHandles(2), []); - else - % standard movie playing - UI.TogglePlay(src,evnt); - end -elseif strcmp(evnt.Key,'period') - if ( ~isempty(ResegState) ) - % Reseg Forward 1 Frame - buttonHandles = get(ResegState.toolbar, 'UserData'); - toggleFunc = get(buttonHandles(3), 'ClickedCallback'); - toggleFunc(buttonHandles(3), []); - else - % step forward one frame - time = Figures.time + 1; - UI.TimeChange(time); - end -elseif strcmp(evnt.Key,'comma') - if ( ~isempty(ResegState) ) - % Reseg Backward 1 Frame - buttonHandles = get(ResegState.toolbar, 'UserData'); - toggleFunc = get(buttonHandles(1), 'ClickedCallback'); - toggleFunc(buttonHandles(1), []); - else - % step backward one frame - time = Figures.time - 1; - UI.TimeChange(time); - end - elseif ( strcmp(evnt.Key,'control') ) - if(~Figures.controlDown) - %prevent this from getting reset when moving the mouse - Figures.controlDown = true; - end -elseif ( strcmp(evnt.Key,'delete') || strcmp(evnt.Key,'backspace') ) - deleteSelectedCells(); -elseif (strcmp(evnt.Key,'f12')) - Figures.cells.showInterior = ~Figures.cells.showInterior; - UI.DrawCells(); - UI.DrawTree(Figures.tree.familyID); -elseif ( strcmp(evnt.Key,'return') ) - mergeSelectedCells(); -end -end - -function figureKeyRelease(src,evnt) - global Figures - - Figures.controlDown = false; -end - -function figureTreeDown(src,evnt) - global Figures indicatorMotionListener indicatorMouseUpListener; - - indicatorMotionListener = addlistener(Figures.tree.handle, 'WindowMouseMotion', @figureTreeMotion); - indicatorMouseUpListener = addlistener(Figures.tree.handle, 'WindowMouseRelease', @indicatorMouseUp); - if(strcmp(get(Figures.tree.handle,'SelectionType'),'normal')) - moveLine(); - end -end - -% NLS - 6/8/12 -% this actually returns the tracks that the hulls belong to -% I'll Hopefully refactor DrawCells to allow drawing specific hulls soon -function likelyHulls = FindLowestCostHulls(parentTrackID, time) -global CellTracks HashedCells CellHulls Figures - parentTrack = CellTracks(parentTrackID); - - fromHullID = Tracks.GetHullID(parentTrack.endTime, parentTrackID); - potentialHulls = HashedCells{time}; - potentialHulls = [potentialHulls.hullID]; - - timeDiff = time - CellHulls(fromHullID).time; - [paths, costs] = mexDijkstra('matlabExtend', fromHullID, abs(timeDiff)+1, @(startID,endID)((any(endID == potentialHulls))), timeDiff); - - likelyHulls = []; - if(length(paths) > 1) - %if cost difference between {1} and {2} is really large, attempt to - %split? - likelyHulls = [paths{1}(end), paths{2}(end)]; - elseif (length(paths) == 1) - likelyHulls = [paths{1}(end)]; - end - - if(length(likelyHulls) == 1) %attempt to split - newHulls = Segmentation.ResegmentHull(CellHulls(likelyHulls(1)), 2, 1, 1); - likelyHulls = []; - if(isempty(newHulls)),return,end - hull1 = newHulls(1); - hull2 = newHulls(2); - Figures.cells.PostDrawHookOnce{end+1} = @(Ax) (plot(Ax, hull1.points(:,1), hull1.points(:,2), 'Color', [.2 .2 .2], 'LineStyle', '-', 'LineWidth', 1.5)); - Figures.cells.PostDrawHookOnce{end+1} = @(Ax) (plot(Ax, hull2.points(:,1), hull2.points(:,2), 'Color', [.8 .8 .8], 'LineStyle', '-', 'LineWidth', 1.5)); - end - - for i=1:length(likelyHulls) - likelyHulls(i) = Hulls.GetTrackID(likelyHulls(i)); - end -end - -% NLS - 6/8/2012 - Created -function mitosisHandleDragging(mitosis) -global Figures CellTracks; - - Y = get(Figures.tree.timeIndicatorLine, 'YData'); - Y = Y(2); - - mitosisHandle = get(mitosis,'UserData'); - %don't allow adjusting a mitosis through other mitoses - minY = CellTracks(mitosisHandle.trackID).startTime + 1; - if (Y <= minY) - return; - end - - children = CellTracks(mitosisHandle.trackID).childrenTracks; - Figures.tree.movingMitosis = [children, FindLowestCostHulls(mitosisHandle.trackID, Y)]; - - %determine how far up/down the user should be allowed to drag a mitosis - if(isempty(CellTracks(children(1)).childrenTracks)) - if(isempty(CellTracks(children(2)).childrenTracks)) - maxY = Inf; - else - maxY = CellTracks(children(1)).endTime - 1; - end - elseif(isempty(CellTracks(children(2)).childrenTracks)) - maxY = CellTracks(children(2)).endTime - 1; - else - maxY = min(CellTracks(children(1)).endTime,CellTracks(children(2)).endTime) - 1; - end - - if (Y >= maxY) - return; - end - - previousMitosisTime = get(mitosisHandle.hLine, 'YData'); - set(mitosisHandle.hLine, 'YData', [Y Y]); -% set(mitosisHandle.diamondHandle, 'YData', Y+1); - - set(mitosisHandle.child1Handles(1), 'YData', Y+1); - text1 = get(mitosisHandle.child1Handles(2), 'Position'); - text1(2) = Y+1; - set(mitosisHandle.child1Handles(2), 'Position', text1); - - set(mitosisHandle.child2Handles(1), 'YData', Y+1); - text2 = get(mitosisHandle.child2Handles(2), 'Position'); - text2(2) = Y+1; - set(mitosisHandle.child2Handles(2), 'Position', text2); -end - -function indicatorMouseUp(src,evt) - global indicatorMotionListener indicatorMouseUpListener Figures CellTracks; - - delete(indicatorMotionListener); - delete(indicatorMouseUpListener); - - if(~isempty(Figures.tree.dragging)) - Y = get(Figures.tree.timeIndicatorLine, 'YData'); - Y = Y(2); - - mitosis = Figures.tree.dragging; - mitosisHandle = get(mitosis,'UserData'); - children = CellTracks(mitosisHandle.trackID).childrenTracks; - - Figures.tree.movingMitosis = []; - %TODO: actually commit tree changes - %GraphEditMoveMitosis(Y, children(1)); - %History('Push'); - %DrawTree(CellTracks(mitosisHandle.trackID).familyID); - Figures.tree.dragging = []; - end -end - -function figureTreeMotion(src,evnt) -global Figures - moveLine(); - UI.DrawCells(); -end - -function moveLine() -global Figures HashedCells -time = get(Figures.tree.axesHandle,'CurrentPoint'); -time = round(time(3)); - -if(strcmp(Figures.advanceTimerHandle.Running,'on')) - UI.TogglePlay([],[]); -end -if(time < 1) - Figures.time = 1; -elseif(time > length(HashedCells)) - Figures.time = length(HashedCells); -else - Figures.time = time; -end -% DrawCells(); -UI.UpdateTimeIndicatorLine(); -end - -function figureTreeUp(src,evnt) -global Figures -if(strcmp(get(Figures.tree.handle,'SelectionType'),'normal')) - UI.TimeChange(Figures.time); -end -end - -function deleteSelectedCells() - global Figures CellFamilies - - bErr = Editor.ReplayableEditAction(@Editor.DeleteCells, Figures.cells.selectedHulls); - if ( bErr ) - return; - end - - UI.ClearCellSelection(); - Error.LogAction(['Removed selected cells [' num2str(Figures.cells.selectedHulls) ']'],Figures.cells.selectedHulls); - - %if the whole family disappears with this change, pick a diffrent family to display - if(isempty(CellFamilies(Figures.tree.familyID).tracks)) - for i=1:length(CellFamilies) - if(~isempty(CellFamilies(i).tracks)) - Figures.tree.familyID = i; - break - end - end - end - - UI.DrawTree(Figures.tree.familyID); - UI.DrawCells(); -end - -function mergeSelectedCells() - global Figures - - [bErr deletedCells replaceCell] = Editor.ReplayableEditAction(@Editor.MergeCells, Figures.cells.selectedHulls); - if ( bErr ) - return; - end - - if ( isempty(replaceCell) ) - msgbox(['Unable to merge [' num2str(Figures.cells.selectedHulls) '] in this frame'],'Unable to Merge','help','modal'); - return; - end - - - UI.ClearCellSelection(); - Error.LogAction('Merged cells', [deletedCells replaceCell], replaceCell); - - UI.DrawTree(Figures.tree.familyID); - UI.DrawCells(); -end - function learnFromEdits(src,evnt) global CellFamilies CellTracks SegmentationEdits Figures diff --git a/src/MATLAB/+UI/KeyStateRelease.m b/src/MATLAB/+UI/KeyStateRelease.m new file mode 100644 index 0000000000000000000000000000000000000000..28c1e2c1cc143dde58cc3604f74e73dcebf9a177 --- /dev/null +++ b/src/MATLAB/+UI/KeyStateRelease.m @@ -0,0 +1,5 @@ +function KeyStateRelease(src,evnt) + global Figures + + Figures.controlDown = false; +end diff --git a/src/MATLAB/+UI/MitosisEditInterface.m b/src/MATLAB/+UI/MitosisEditInterface.m index 5b0b2bf933b85e6c5793339aa4bfaeed6dfbfc6c..fcd9914350cf9386def4b21eff90c71b509edca3 100644 --- a/src/MATLAB/+UI/MitosisEditInterface.m +++ b/src/MATLAB/+UI/MitosisEditInterface.m @@ -14,12 +14,12 @@ function MitosisEditInterface() rootTrackID = CellFamilies(editTree).rootTrackID; firstHull = CellTracks(rootTrackID).hulls(1); - UI.MitosisSelectTrackingCell(rootTrackID,CellHulls(firstHull).time, true); - % Order matters here, we want the Init action to be part of the subtask Editor.ReplayableEditAction(@Editor.StartReplayableSubtask, 'MitosisEditTask'); Editor.ReplayableEditAction(@Editor.MitosisEditInitializeAction, editTree, length(HashedCells)); + UI.MitosisSelectTrackingCell(rootTrackID,CellHulls(firstHull).time, true); + hToolbar = addButtons(); end diff --git a/src/MATLAB/+UI/MitosisSelectPhenotype.m b/src/MATLAB/+UI/MitosisSelectPhenotype.m new file mode 100644 index 0000000000000000000000000000000000000000..4cea8c36dbce0ec34f21e548f54879b440ed28b2 --- /dev/null +++ b/src/MATLAB/+UI/MitosisSelectPhenotype.m @@ -0,0 +1,17 @@ +function [hullID trackID] = MitosisSelectPhenotype() + global Figures MitosisEditStruct + + hullID = []; + trackID = []; + if ( isempty(MitosisEditStruct.selectedTrackID) ) + msgbox('No cells selected for mitosis or phenotype identification','No Cell Selected','warn'); + return; + end + + trackID = MitosisEditStruct.selectedTrackID; + + currentPoint = UI.GetClickedCellPoint(); + [bErr hullID] = Editor.ReplayableEditAction(@Editor.MitosisHullPhenotypeAction, currentPoint, Figures.time, trackID); + + trackID = Hulls.GetTrackID(hullID); +end diff --git a/src/MATLAB/+UI/MoveLine.m b/src/MATLAB/+UI/MoveLine.m new file mode 100644 index 0000000000000000000000000000000000000000..f06beb1305770c099e118e6ce7a11f74d8675049 --- /dev/null +++ b/src/MATLAB/+UI/MoveLine.m @@ -0,0 +1,20 @@ +function MoveLine() + global Figures HashedCells + + time = get(Figures.tree.axesHandle,'CurrentPoint'); + time = round(time(3)); + + if(strcmp(Figures.advanceTimerHandle.Running,'on')) + UI.TogglePlay([],[]); + end + + if(time < 1) + Figures.time = 1; + elseif(time > length(HashedCells)) + Figures.time = length(HashedCells); + else + Figures.time = time; + end + + UI.UpdateTimeIndicatorLine(); +end diff --git a/src/MATLAB/+UI/Play.m b/src/MATLAB/+UI/Play.m new file mode 100644 index 0000000000000000000000000000000000000000..706adb458b8a2be80d3cd92283103de6a7ca3a21 --- /dev/null +++ b/src/MATLAB/+UI/Play.m @@ -0,0 +1,11 @@ +function Play(src,evt) + global Figures HashedCells + + time = Figures.time + 1; + + if(time >= length(HashedCells)) + time = 1; + end + + UI.TimeChange(time); +end diff --git a/src/MATLAB/+UI/UpdatePhenotypeMenu.m b/src/MATLAB/+UI/UpdatePhenotypeMenu.m index e52115d31de765050138361355183c673e80e688..ecbdfc19b756392f854207eadd48507e9c85dc61 100644 --- a/src/MATLAB/+UI/UpdatePhenotypeMenu.m +++ b/src/MATLAB/+UI/UpdatePhenotypeMenu.m @@ -45,9 +45,13 @@ end function setPhenotype(src, evnt) global Figures CellPhenotypes - [hullID trackID] = UI.GetClosestCell(0); - if(isempty(trackID)) - return + if ( strcmpi(Figures.cells.editMode, 'mitosis') ) + [hullID trackID] = UI.MitosisSelectPhenotype(); + else + [hullID trackID] = UI.GetClosestCell(0); + if(isempty(trackID)) + return + end end clickPheno = get(src, 'UserData');