function View(tr,sel,propsForFigure) %VIEW views a phylogenetic tree in phytreetool. % % VIEW(TREE) shows a phylogenetic tree object. The significant distances % between branches and nodes are in horizontal direction, vertical % coordinates are accommodated only for display purposes. Tree % Edit/Analysis tools are accessible through the mouse left/right buttons % and also using the 'Tree' menu. % % VIEW(TREE,SEL) starts the viewer with an initial selection of nodes % specified by SEL. SEL can be a logical array of any of the following % sizes: [NUMLEAVES+NUMBRANCHES x 1], [NUMLEAVES x 1], or [NUMBRANCHES x % 1]. SEL may also be a list of indices. % % Examples: % % tr = phytreeread('pf00002.tree') % view(tr) % % See also PHYTREE, PHYTREE/PLOT, PHYTREEREAD, PHYTREETOOL, SEQLINKAGE, % SEQNEIGHJOIN. % Copyright 2003-2006 The MathWorks, Inc. % $Revision: 1.1.6.19.2.1 $ $Author: batserve $ $Date: 2006/07/24 13:54:28 $ if numel(tr)~=1 error('Bioinfo:phytree:view:NoMultielementArrays',... 'Phylogenetic tree must be an 1-by-1 object.'); end tr = doBasicCalculations(tr); nodeIndex = 1:tr.numLabels; leafIndex = 1:tr.numLeaves; branchIndex = tr.numLeaves+1:tr.numLabels; % check empty names for ind = nodeIndex if isempty(tr.names{ind}) if ind > tr.numLeaves tr.names{ind} = ['Branch ' num2str(ind-tr.numLeaves)]; else tr.names{ind} = ['Leaf ' num2str(ind)]; end end end % initial drawing if nargin<3 propsForFigure.Name = ['Phylogenetic Tree Tool ' getphytreetoolnumber]; end propsForFigure.PruneWarning = getacceptedwarningfromothertools; fig = figure('Renderer','ZBuffer','Name',propsForFigure.Name,... 'NumberTitle','off','IntegerHandle','off','tag','PhyTreeTool'); setappdata(fig,'propsForFigure',propsForFigure) setappdata(fig,'backupTree',tr) tr.ha = axes; hold on; set(tr.ha,'Position',[.05 .05 .7 .9],'YTick',leafIndex,'FontSize',9,'Ydir','reverse',... 'YAxisLocation','Right','YTickLabel',char(tr.names{leafIndex})) tr.hlines = plot( ... tr.x([nodeIndex;repmat([tr.par(1:tr.numLabels-1) tr.numLabels],2,1)]),... tr.y([repmat(nodeIndex,2,1);[tr.par(1:tr.numLabels-1) tr.numLabels]]),... '-k'); tr.hpathline = plot(1,1,'--r','LineWidth',2,'Visible','off'); tr.hdragbox = plot(1,1,':k','LineWidth',1,'Visible','off'); tr.hdots(1,1) = plot(tr.x(branchIndex),tr.y(branchIndex),'o',... 'MarkerSize',5,'MarkerEdgeColor','k','MarkerFaceColor','b'); tr.hdots(1,2) = plot(tr.x(leafIndex),tr.y(leafIndex),'square',... 'MarkerSize',4,'MarkerEdgeColor','k','MarkerFaceColor','w'); tr.hseldots(1,1) = plot(tr.x(branchIndex),tr.y(branchIndex),'o',... 'MarkerSize',5,'MarkerEdgeColor','r','MarkerFaceColor','r'); tr.hseldots(1,2) = plot(tr.x(leafIndex),tr.y(leafIndex),'square',... 'MarkerSize',4,'MarkerEdgeColor','r','MarkerFaceColor','r'); tr.hldots(1,1) = plot(tr.x(branchIndex),tr.y(branchIndex),'o',... 'MarkerSize',5,'MarkerEdgeColor',[.5 .5 .5],... 'MarkerFaceColor',[.6 .6 1]); tr.hldots(1,2) = plot(tr.x(leafIndex),tr.y(leafIndex),'square',... 'MarkerSize',4,'MarkerEdgeColor',[.5 .5 .5],... 'MarkerFaceColor','w'); set(tr.hldots(1),'Xdata',[],'Ydata',[]) set(tr.hldots(2),'Xdata',[],'Ydata',[]) tr.axhold = plot([-eps -eps],[0 0],'.','MarkerSize',eps,'Color','w'); tr.datatip = text(0,1,1,'k','Tag','TreeTag','BackgroundColor',[1 1 .93],... 'Color', [0 0 0],'EdgeColor', [0.8 0.8 0.8],... 'VerticalAlignment','Top','Clipping','off',... 'Visible','off','Fontsize',8,'Interpreter','none'); if nargin == 1 || isempty(sel) tr.selected = false(tr.numLabels,1); % selected nodes else % validate sel if islogical(sel) if numel(sel)==tr.numLabels sel = sel(:)==true; elseif numel(sel)==tr.numLeaves sel = [sel(:);false(tr.numBranches,1)]; elseif numel(sel)==tr.numBranches sel = [false(tr.numLeaves,1);sel(:)]; else close(fig) error('Bioinfo:phytree:view:IncorrectLogical',... 'Logical vector must have the same number of elements as nodes in the Phylogenetic Tree'); end elseif isnumeric(sel) && isreal(sel) && all(sel>=1) && all(sel<=tr.numLabels) tem(tr.numLabels)=false; tem(floor(sel))=true; sel=tem(:); else close(fig) error('Bioinfo:phytree:view:IncorrectTypeofArguments',... 'Invalid value for NODES'); end tr.selected =sel; end % save more figure data needed for the gui functionality tr.activeNodes = true(tr.numLabels,1); % active nodes tr.activeBranches = true(tr.numBranches,1); % active Branches tr.sel2root = false(tr.numLabels,1); % path sel-node to root tr.editMode = 'Select'; % initial edit mode tr.indicativeMode = false; % data-tip flag tr.lastThresholdValue = []; % remembers last cut % create uicontrols (will appear as needed, initially invisible) tr.editBox = uicontrol(fig,'Background',[1 1 1],'style','edit',... 'visible','off','callback',@doneRenaming); tr.slider = uicontrol(fig,'style','slider','SliderStep',[.1 .1],... 'visible','off','callback',@sliderCallback); tr.slidertx = uicontrol(fig,'style','text','visible','off'); tr.sliderok = uicontrol(fig,'style','pushbutton','visible','off',... 'string','OK','callback',@doThresholdCut); % setup callback for click over nodes set([tr.hseldots,tr.hdots,tr.hldots],'ButtonDownFcn',@toggleNode) % setup figure callback functions set(fig,'WindowButtonDownFcn',@mouseClickOnFigure); set(fig,'WindowButtonUpFcn',@mouseRelease); set(fig,'WindowButtonMotionFcn',@localWindowButtonMotion); % setup UIMenus, context menus and toolbar tr.hToggleUIMenu = makePhyTreeViewerUIMenus(fig); tr.hToggleToolbar = makePhyTreeViewerToolbar(fig); [tr.hToggleContextMenu,tr.hAxisContextMenu,tr.hDotsContextMenu] = ... makePhyTreeViewerContextMenus(fig); % activate Context Menus set(tr.ha,'UIContextMenu',tr.hAxisContextMenu); set([tr.hdots tr.hldots tr.hseldots],'UIContextMenu',tr.hDotsContextMenu); set(fig,'UserData',tr) % save figure data correctFigureSize(fig, 15 * tr.numLeaves); % resize figure if needed setupYLabelsListeners; % listeners for YLabels updateTree(fig,[],[]) % updates figure after all initializations set(gca,'xLim',[0 max(tr.x)] + max(tr.x) * [-.1 .05]); tr.yLim = get(tr.ha,'Ylim');tr.xLim = get(tr.ha,'Xlim'); set(fig,'UserData',tr) % save figure data toolsmenufcn(fig,'PanY') % set zoom mode to vertical constraining toolsmenufcn(fig,'ZoomY') % set pan mode to vertical constraining set(fig,'HandleVisibility','callback') % after all init, make it invisible %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function ylabelsListener(hSrc,event,hf,ha) %#ok % Auto sizes the ylabels ratio = max(get(hf,'Position').*[0 0 0 1])/diff(get(ha,'YLim')); set(ha,'Fontsize',min(9,ceil(ratio/1.7))); % the gold formula % Also verify if we need to re-position the slidebar of threshold cut tr=get(hf,'Userdata'); if any(strcmp(tr.editMode,{'Distance to Leaves','Distance to Root'})) wS = get(hf,'Position'); % window dimensions aP = get(tr.ha,'Position'); % axes position set(tr.slider, 'Position',[aP(1)*wS(3) wS(4)-20 aP(3)*wS(3) 20]) set(tr.slidertx,'Position',[sum(aP([1 3]))*wS(3) wS(4)-20 60 20]) set(tr.sliderok,'Position',[sum(aP([1 3]))*wS(3)+60 wS(4)-20 30 20]) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function mouseClickOnFigure(h,varargin) % This callback function is activated when a mouse button is pressed in any % location of the figure and under any of my edit modes tr = get(gcbf,'Userdata'); switch tr.editMode case 'Renaming'; doneRenaming(h,varargin); case 'Distance to Leaves'; cancelThresholdCut(h,varargin); case 'Distance to Root'; cancelThresholdCut(h,varargin); case 'Select'; switch get(gcbf,'SelectionType') case {'normal','extend'} tr = get(gcbf,'userdata'); cp = get(tr.ha,'CurrentPoint'); xPos = cp(1,1); yPos = cp(1,2); set(tr.hdragbox,'Visible','on',... 'Xdata',repmat(xPos,5,1),'Ydata',repmat(yPos,5,1)) case 'open' autoFit(h) end end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function toggleNode(h,varargin) % This callback function is activated when a mouse button is pressed over % any of the displayed nodes under any of my edit modes hideActiveIndicators(h,varargin) tr = get(gcbf,'Userdata'); switch get(gcbf,'SelectionType') case 'normal' switch tr.editMode case 'Select'; selectNode(h,varargin); case 'Inspect'; inspectNode(h,varargin); case 'Collapse/Expand'; collapseExpand(h,varargin); case 'Rotate Branch'; rotateBranch(h,varargin); case 'Rename'; renameNode(h,varargin); case 'Renaming'; doneRenaming(h,varargin); case 'Prune'; pruneTree(h,varargin); case 'Distance to Leaves'; cancelThresholdCut(h,varargin); case 'Distance to Root'; cancelThresholdCut(h,varargin); end case 'extend' switch tr.editMode case 'Select'; selectNode(h,varargin); end case 'alt' case 'open' end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function changeEditMode(h,varargin) %#ok % Callback function to change the edit mode, this function is % called from the toolbar, the context menu or the uimenu. tr = get(gcbf,'Userdata'); myModes = {'Inspect','Collapse/Expand','Rotate Branch','Rename','Prune'}; % first, disable any present edit mode switch tr.editMode case {myModes{:},'Select'}; disableMyContextMenus(h) disableMyWindowButtonActions(h) ind = strmatch(tr.editMode,myModes); %#ok set(tr.hToggleToolbar(ind), 'State','off') set(tr.hToggleUIMenu(ind), 'Checked','off') set(tr.hToggleContextMenu(ind),'Checked','off') %case '&Zoom In'; toolsmenufcn(gcbf,'ZoomIn'); case '&Zoom In'; zoom(gcbf,'off') %case 'Zoom &Out'; toolsmenufcn(gcbf,'ZoomOut'); case 'Zoom &Out'; zoom(gcbf,'off') %case '&Pan'; toolsmenufcn(gcbf,'Pan'); case '&Pan'; pan(gcbf,'off'); case {'Distance to Leaves','Distance to Root'} enableAllUI(h) disableMyWindowButtonActions(h) end % depending on the caller instance, determine the new edit mode switch get(h,'Type') case 'uimenu'; newEditMode = get(h,'Label'); case 'uitoggletool' newEditMode = get(h,'Tag'); switch newEditMode case 'Exploration.ZoomIn'; newEditMode = '&Zoom In'; case 'Exploration.ZoomOut'; newEditMode = 'Zoom &Out'; case 'Exploration.Pan'; newEditMode = '&Pan'; end otherwise; newEditMode = 'Select'; end %disp( [tr.editMode ' --> ' newEditMode] ) % if new mode is the same then we are toggling off if strcmp(newEditMode,tr.editMode) newEditMode = 'Select'; end % if changing to Prune, verify the warnign has been accepted if strcmp(newEditMode,'Prune') propsForFigure = getappdata(gcbf,'propsForFigure'); if isequal(propsForFigure.PruneWarning,'NotDone') warndlg(['Pruning nodes cannot be undone. Before continuing,',... ' you may want to export the current tree to a new tool.'],... 'Warning','modal') setacceptedwarningtoothertools end end switch newEditMode case '&Zoom In'; toolsmenufcn(gcbf,'ZoomIn'); case 'Zoom &Out'; toolsmenufcn(gcbf,'ZoomOut'); %case '&Pan'; toolsmenufcn(gcbf,'Pan'); case '&Pan'; pan(gcbf,'on'); case myModes; enableMyContextMenus(h) enableMyWindowButtonActions(h) ind = strmatch(newEditMode,myModes); %#ok set(tr.hToggleToolbar(ind), 'State','on') set(tr.hToggleUIMenu(ind), 'Checked','on') set(tr.hToggleContextMenu(ind),'Checked','on') case 'Select'; enableMyContextMenus(h) enableMyWindowButtonActions(h) case {'Distance to Leaves','Distance to Root'} disableAllUI(h) enableMyWindowButtonActions(h) set(gcbf,'WindowButtonMotionFcn',[]) set(gcbf,'WindowButtonUpFcn',[]) set([tr.hseldots,tr.hdots,tr.hldots],'ButtonDownFcn',[]) end switch newEditMode case 'Inspect'; if sum(tr.selected(:)) ~= 1 tr.selected(:) = false; tr.selected(end) = true; end otherwise end tr.sel2root = path2root(tr, tr.selected); tr.editMode = newEditMode; set(gcbf,'userdata',tr) updateTree(gcbf,[],[]) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function hideActiveIndicators(h,varargin) %#ok tr = get(gcbf,'userdata'); set([tr.hpathline,tr.datatip],'visible','off') %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function disableAllUI(h,varargin) %#ok hw = findall(gcbf,'Type','uimenu','Parent',gcbf); gw = findall(gcbf,'Type','UIToggleTool'); set([hw;gw],'Enable','off') %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function enableAllUI(h,varargin) %#ok hw = findall(gcbf,'Type','uimenu','Parent',gcbf); gw = findall(gcbf,'Type','UIToggleTool'); set([hw;gw],'Enable','on') %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function disableMyContextMenus(h,varargin) %#ok tr = get(gcbf,'userdata'); set(tr.ha,'UIContextMenu',[]); set([tr.hdots tr.hseldots],'UIContextMenu',[]); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function enableMyContextMenus(h,varargin) %#ok tr = get(gcbf,'userdata'); set(tr.ha,'UIContextMenu',tr.hAxisContextMenu); set([tr.hdots tr.hldots tr.hseldots],'UIContextMenu',tr.hDotsContextMenu); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function disableMyWindowButtonActions(h,varargin) %#ok set(gcbf,'WindowButtonDownFcn',[]); set(gcbf,'WindowButtonUpFcn',[]) set(gcbf,'WindowButtonMotionFcn',[]); tr = get(gcbf,'userdata'); set([tr.hseldots,tr.hdots,tr.hldots],'ButtonDownFcn',[]) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function enableMyWindowButtonActions(h,varargin) %#ok set(gcbf,'WindowButtonDownFcn',@mouseClickOnFigure); set(gcbf,'WindowButtonUpFcn',@mouseRelease); set(gcbf,'WindowButtonMotionFcn',@localWindowButtonMotion); tr = get(gcbf,'userdata'); set([tr.hseldots,tr.hdots,tr.hldots],'ButtonDownFcn',@toggleNode) set(gcbf,'KeyPressFcn',[]); set(gcbf,'Pointer','arrow'); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function localWindowButtonMotion(h,varargin) %#ok % Callback function activated when moving over the axes, checks location of % the mouse and puts datatip if over an active node. tr = get(h,'userdata'); % set a virtual grid to get the point xThres=diff(get(tr.ha,'Xlim'))/100; yThres=diff(get(tr.ha,'Ylim'))/100; cp = get(tr.ha,'CurrentPoint'); xPos = cp(1,1); yPos = cp(1,2); hp = tr.x<(xPos+xThres) & tr.x>(xPos-xThres) & ... tr.y<(yPos+yThres) & tr.y>(yPos-yThres); hp = find (hp & tr.activeNodes); % shortcut out when dragging a box in select mode if strcmp(get(tr.hdragbox,'Visible'),'on') xdata = get(tr.hdragbox,'XData');xdata([3,4]) = xPos; ydata = get(tr.hdragbox,'YData');ydata([2,3]) = yPos; set(tr.hdragbox,'XData',xdata,'YData',ydata) % shortcut out when turning off 'indicative' mode elseif tr.indicativeMode && isempty(hp) %&& isempty(tr.highligth) set([tr.datatip tr.hpathline],'visible','off') set(tr.hlines,'color','black') set(tr.hldots(1),'Xdata',[],'Ydata',[]) set(tr.hldots(2),'Xdata',[],'Ydata',[]) % turn on or update 'indicative' mode elseif numel(hp) % && isempty(tr.highligth) % find leaves (children) below this branch children = false(1,tr.numLabels); children(hp(1)) = true; for ind = hp(1)-tr.numLeaves:-1:1 if children(ind+tr.numLeaves) children(tr.tree(ind,:))=true; end end % find and draw path to selected if strcmp(tr.editMode,'Inspect') [pathA,pathB] = path2sel(tr,hp(1)); dis2sel = tr.x(find(pathA,1))+tr.x(find(pathB,1))... -2*tr.x(find(pathA,1,'last')); if any(pathB) xx = [tr.x(pathA);NaN;tr.x(pathB)]; yy = [tr.y(pathA);NaN;tr.y(pathB)]; hh=zeros(2*numel(xx),1); hh(1:2:end)=1; hh=cumsum(hh); set(tr.hpathline,'XData',xx(hh(2:end)),... 'YData',yy(hh(1:end-1)),'Visible','on'); end end % place text name = [tr.names{hp(1)} ' ']; name(name=='_')=' '; children(hp(1)) = false; numChil = sum(children(1:tr.numLeaves)); childrenNames = char(tr.names(children(1:tr.numLeaves))); childrenNames(childrenNames=='_')=' '; childrenNames=[repmat(' ',size(childrenNames,1),1) childrenNames]; switch tr.editMode case 'Inspect' if numChil set(tr.datatip,'string',char( [ {name; ... ['Dist to parent: ' num2str(tr.dist(hp(1)))];... ['Dist to root: ' num2str(tr.x(hp(1))-tr.x(end))];... ['Path length: ' num2str(dis2sel)];... ['Samples: ' num2str(numChil)]};... mat2cell(childrenNames,ones(size(childrenNames,1),1),... size(childrenNames,2))])) extraLines = 5; else set(tr.datatip,'string',char( {name; ... ['Dist to parent: ' num2str(tr.dist(hp(1)))];... ['Dist to root: ' num2str(tr.x(hp(1))-tr.x(end))];... ['Path length: ' num2str(dis2sel)]})) extraLines = 4; end case {'Collapse/Expand','Rotate Branch','Rename','Prune','Select'} if numChil set(tr.datatip,'string',char([ {[name ' (' num2str(numChil) ' samples)']};... mat2cell(childrenNames,ones(size(childrenNames,1),1),... size(childrenNames,2))])) else set(tr.datatip,'string', name) end extraLines = 1; otherwise % all other modes end %compute some values before adjusting data tip fp = get(gcbf,'Position'); % fig position in points fh = fp(4);%fw = fp(3); % fig size (height & width) in points ap = get(tr.ha,'Position'); % axis position normalized yl = ylim(tr.ha); yl = yl - ... [ap(2) ap(2)+ap(4)-1]*diff(yl)/ap(4); % fig height limits in axis units xl = xlim(tr.ha); xl = xl - ... [ap(1) ap(1)+ap(3)-1]*diff(xl)/ap(3); % fig width limits in axis units yPosPt = (-yPos -4*yThres + yl(2))*fh/diff(yl); % datatip position in pts reqPt = (numChil+extraLines)*14+2; % required datatip height in pts % adjust if other fontsize is used %adjust string of datatip if it will not fit (i.e. remove names) if reqPt > fh str = get(tr.datatip,'String'); set(tr.datatip,'String',str(1:extraLines,:)); reqPt = extraLines*14+2; end %adjust vertical position of datatip just below cp topEdge = yl(2)-min(fh,max(yPosPt,reqPt))*diff(yl)/fh; switch tr.editMode case {'Collapse/Expand','Rotate Branch','Prune'} % datatip usually to the left of cp to see shadowing of branches datatipExtent = get(tr.datatip,'Extent'); datatipWidth = datatipExtent(3); rightEdge = max(xPos-3*xThres,xl(1)+datatipWidth); % is the datatip over cp ? if rightEdge>xPos && topEdgemin(xdata) & ... tr.ymin(ydata) & tr.activeNodes) ; if (strcmp(get(gcbf,'SelectionType'),'normal') && ... strcmp(get(tr.hdragbox,'visible'),'on')) tr.selected(:) = false; end tr.selected(hp) = true; tr.sel2root = path2root(tr,tr.selected); set(tr.hdragbox,'Visible','off') set(gcbf,'userdata',tr) updateTree(gcbf,[],[]) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function selectNode(h,varargin) %#ok % Callback function to select a Node. % Entry points: from 1) the dots context menu or 2) toggle node tr = get(gcbf,'userdata'); % set a virtual grid to get the point xThres=diff(get(tr.ha,'Xlim'))/100; yThres=diff(get(tr.ha,'Ylim'))/100; cp = get(tr.ha,'CurrentPoint'); xPos = cp(1,1); yPos = cp(1,2); hp = find(tr.x<(xPos+xThres) & tr.x>(xPos-xThres) & ... tr.y<(yPos+yThres) & tr.y>(yPos-yThres)) ; if numel(hp) set(tr.hdragbox,'visible','off') temp = tr.selected(hp(1)); switch get(gcbf,'SelectionType') case 'normal'; tr.selected(:) = false; case 'alt'; if ~temp tr.selected(:) = false; end temp=false; end tr.selected(hp(1)) = ~temp; tr.sel2root = path2root(tr,tr.selected); set(gcbf,'userdata',tr) updateTree(gcbf,[],[]) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function inspectNode(h,varargin) %#ok % Callback function to inspect the reference Node. % Entry points: from 1) the dots context menu or 2) toggle node tr = get(gcbf,'userdata'); % set a virtual grid to get the point xThres=diff(get(tr.ha,'Xlim'))/100; yThres=diff(get(tr.ha,'Ylim'))/100; cp = get(tr.ha,'CurrentPoint'); xPos = cp(1,1); yPos = cp(1,2); hp = find(tr.x<(xPos+xThres) & tr.x>(xPos-xThres) & ... tr.y<(yPos+yThres) & tr.y>(yPos-yThres)) ; if numel(hp) temp = tr.selected(hp(1)); tr.selected(:) = false; tr.selected(hp(1)) = ~temp; tr.sel2root = path2root(tr,tr.selected); set(gcbf,'userdata',tr) updateTree(gcbf,[],[]) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function collapseExpand(h,varargin) %#ok % Callback function to Collapse/Expand a branch. % Entry points: from 1) the dots context menu or 2) toggle node tr = get(gcbf,'userdata'); if strcmp(get(h,'Type'),'uimenu') % come from a context menu hp = find(tr.selected(tr.numLeaves+1:tr.numLabels)); else % set a virtual grid to get the point xThres=diff(get(tr.ha,'Xlim'))/100; yThres=diff(get(tr.ha,'Ylim'))/100; cp = get(tr.ha,'CurrentPoint'); xPos = cp(1,1); yPos = cp(1,2); hp = find(tr.x<(xPos+xThres) & tr.x>(xPos-xThres) & ... tr.y<(yPos+yThres) & tr.y>(yPos-yThres)) ; hp=hp(hp>tr.numLeaves)-tr.numLeaves; if numel(hp) hp=hp(1); %just in case it picked two points end end if numel(hp) for ind = 1:numel(hp) tr.activeBranches(hp(ind))=~tr.activeBranches(hp(ind)); activeBranches=find(tr.activeBranches)'; % find active nodes by expanding active Branches tr.activeNodes(:)=false; tr.activeNodes(tr.numLabels,1)=true; for k = activeBranches(end:-1:1) tr.activeNodes(tr.tree(k,:))=tr.activeNodes(k+tr.numLeaves); end tr.selected(:) = false; tr.selected(hp(ind)+tr.numLeaves) = true; end tr.sel2root = path2root(tr,tr.selected); set(gcbf,'userdata',tr) updateTree(gcbf,[],hp(end)+tr.numLeaves) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function rotateBranch(h,varargin) %#ok % Callback function to rotate a branch reordering the leaves. % Entry points: from 1) the dots context menu or 2) toggle node tr = get(gcbf,'userdata'); if strcmp(get(h,'Type'),'uimenu') % come from a context menu hp = find(tr.selected(tr.numLeaves+1:tr.numLabels)); else % set a virtual grid to get the point xThres=diff(get(tr.ha,'Xlim'))/100; yThres=diff(get(tr.ha,'Ylim'))/100; cp = get(tr.ha,'CurrentPoint'); xPos = cp(1,1); yPos = cp(1,2); hp = find(tr.x<(xPos+xThres) & tr.x>(xPos-xThres) & ... tr.y<(yPos+yThres) & tr.y>(yPos-yThres)) ; hp=hp(hp>tr.numLeaves)-tr.numLeaves; if numel(hp) hp=hp(1); %just in case it picked two points end end if numel(hp) for ind = 1:numel(hp) %find Leaves for every child childrenA = false(1,tr.numLabels); childrenA(tr.tree(hp(ind),1)) = true; for k = tr.tree(hp(ind),1)-tr.numLeaves:-1:1 if childrenA(k+tr.numLeaves) childrenA(tr.tree(k,:))=true; end end childrenB = false(1,tr.numLabels); childrenB(tr.tree(hp(ind),2)) = true; for k = tr.tree(hp(ind),2)-tr.numLeaves:-1:1 if childrenB(k+tr.numLeaves) childrenB(tr.tree(k,:))=true; end end permuta = 1:tr.numLabels; chA = find(childrenA(1:tr.numLeaves)); chB = find(childrenB(1:tr.numLeaves)); if chA(1)(xPos-xThres) & ... tr.y<(yPos+yThres) & tr.y>(yPos-yThres)) ; if numel(hp) tr.previousMode = tr.editMode; tr.editMode = 'Renaming'; xBoxPos = (.02+(xPos-Xlim(1))/diff(Xlim))*aPos(3)+aPos(1); yBoxPos = (.02+(Ylim(2)-yPos)/diff(Ylim))*aPos(4)+aPos(2); position=get(gcbf,'position'); position=[position(3)*xBoxPos position(4)*yBoxPos 150 20]; set(tr.editBox,'position',position); set(tr.editBox,'Visible','on','string',tr.names{hp(1)},'Value',hp(1)) disableAllUI(h) disableMyContextMenus(h) set(gcbf,'WindowButtonMotionFcn',[]); % disable windows mouse motion set(gcbf,'userdata',tr) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function doneRenaming(h,varargin) %#ok % Output helper function to abandon the "Renaming" mode tr = get(gcbf,'userdata'); tr.editMode = tr.previousMode; tr.names{get(tr.editBox,'Value')} = get(tr.editBox,'String'); set(tr.editBox,'Visible','off') set(gcbf,'userdata',tr) updateTree(gcbf,[],[]); enableAllUI(h) enableMyContextMenus(h) enableMyWindowButtonActions(h) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function pruneTree(h,varargin) %#ok % Callback function to prune the tree, this function is complex because not % only the basic structure is updated but all the other handles are % also updated to contain the new tree. % if changing to Prune, verify the warning has been accepted propsForFigure = getappdata(gcbf,'propsForFigure'); if isequal(propsForFigure.PruneWarning,'NotDone') warndlg(['Pruning nodes cannot be undone. Before continuing,',... ' you may want to export the current tree to a new tool.'],... 'Warning','modal') setacceptedwarningtoothertools return % do not do this pruning end tr = get(gcbf,'userdata'); if strcmp(get(h,'Type'),'uimenu') % comes from a context menu hp = find(tr.selected); else tr = get(gcbf,'userdata'); Xlim=get(tr.ha,'Xlim');Ylim=get(tr.ha,'Ylim'); % aPos=get(tr.ha,'Position'); % set a virtual grid to get the point xThres=diff(Xlim)/100; yThres=diff(Ylim)/100; cp = get(tr.ha,'CurrentPoint'); xPos = cp(1,1); yPos = cp(1,2); hp = find(tr.x<(xPos+xThres) & tr.x>(xPos-xThres) & ... tr.y<(yPos+yThres) & tr.y>(yPos-yThres)); hp=hp(1); %just in case it picked two points end hp(hp==tr.numLabels)=[]; %cannot delete the root while numel(hp) %find all nodes to purge (i.e. all descendants) children = false(1,tr.numLabels); children(hp(1)) = true; for k = hp(1)-tr.numLeaves:-1:1 if children(k+tr.numLeaves) children(tr.tree(k,:))=true; end end mypar = tr.par(hp(1)); % parent if mypar < tr.numLabels % my parent is NOT the root % connect brother to granparent mygrpar = tr.par(mypar); % grandparent myuncle = setxor(tr.tree(mygrpar-tr.numLeaves,:),mypar); % uncle mybro = setxor(tr.tree(mypar-tr.numLeaves,:),hp(1)); % brother tr.tree(mygrpar-tr.numLeaves,:) = [myuncle mybro]; tr.dist(mybro) = tr.dist(mybro) + tr.dist(mypar); temp = get(tr.hlines(mygrpar-tr.numLeaves),'Xdata'); temp([1 4])=tr.x(tr.tree(mygrpar-tr.numLeaves,:)); set(tr.hlines(mygrpar-tr.numLeaves),'Xdata',temp); highlight = [mybro,mygrpar]; else % if my parent is the root, now I am the new root temp=cell2mat(get(tr.hlines,'Xdata'))-tr.dist(end); for k = 1:tr.numBranches set(tr.hlines(k),'Xdata',temp(k,:)); end highlight = setxor(tr.tree(mypar-tr.numLeaves,:),hp(1)); end children(mypar) = true; %also delete my par % find indexes to change tree permuta = 1:tr.numLabels; permuta(children) = []; ipermuta = zeros(1,tr.numLabels); ipermuta(permuta) = 1:length(permuta); permutaBranches = permuta(permuta>tr.numLeaves)-tr.numLeaves; % update all tree structure fields tr.names = tr.names(permuta); tr.dist = tr.dist(permuta); tr.tree = tr.tree(permutaBranches,:); tr.tree = ipermuta(tr.tree); if isempty(tr.tree) return; end % one leaf, no branches ! tr = doBasicCalculations(tr); hlines = tr.hlines; tr.hlines = tr.hlines(permuta); delete(setxor(hlines,tr.hlines)); tr.activeNodes = tr.activeNodes(permuta); tr.activeBranches = tr.activeBranches(permutaBranches); tr.selected = tr.selected(permuta); tr.selected(:) = false; tr.selected(ipermuta(highlight)) = true; tr.sel2root = path2root(tr,tr.selected); % update the vector with nodes to prune (node index has changed) hp=ipermuta(hp); hp(1)=[]; hp(hp==0)=[]; set(gcbf,'userdata',tr) updateTree(gcbf,[],[]) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function colorDown(h,varargin) %#ok % Color Down a branch. % Entry points: from 1) the dots context menu or 2) toggle node disp('Color Down a branch. Not implemented YET !!!') %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function findNode(h,varargin) %#ok treefig = gcbf; tr = get(treefig,'userdata'); s = inputdlg('Regular Expression to match ?','Find Leaf/Branch',1); if ~isempty(s) hc=regexpi(regexprep(tr.names,'_',' '),s); h = false(1,tr.numLabels); for ind = 1:tr.numLabels if ~isempty(hc{ind}) h(ind)=true; end end hf = find(h); for ind = 1:length(hf) while ~tr.activeNodes(hf(ind)) hf(ind)=tr.par(hf(ind)); end end tr.selected(:) = false; tr.selected(hf) = true; tr.sel2root = path2root(tr,tr.selected); % update path to root set(treefig,'Userdata',tr); updateTree(treefig,[],[]) % if selected are out of current view then fit the tree if (any(min(ylim(tr.ha))>tr.y(tr.selected)) || ... any(max(ylim(tr.ha))= Value; set(tr.slidertx,'String',num2str(Value)) end set(gcbf,'Userdata',tr); toshow = [tr.tocollapse(tr.par(1:tr.numLabels-1));0]&tr.tocollapse; mask = (1:tr.numLabels)'>tr.numLeaves; % update light lines set(tr.hlines(~toshow),'color','k') set(tr.hlines(toshow),'color',[.87 .87 .87]) % update light dots set(tr.hldots(1),'Ydata',tr.y(toshow&mask&tr.activeNodes),... 'Xdata',tr.x(toshow&mask&tr.activeNodes)) set(tr.hldots(2),'Ydata',tr.y(toshow&~mask&tr.activeNodes),... 'Xdata',tr.x(toshow&~mask&tr.activeNodes)) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function doThresholdCut(h,varargin) %#ok % this helper function inactivates nodes based on the threshold cut selected % with the slider. Entry point: the only way to get into this function is % by the 'OK' uicontrol next to the slider. tr = get(gcbf,'userdata'); tr.activeBranches = tr.activeBranches & ~tr.tocollapse(tr.numLeaves+1:tr.numLabels); % find active nodes by expanding active Branches activeBranches=find(tr.activeBranches)'; tr.activeNodes(:)=false; tr.activeNodes(tr.numLabels,1)=true; for ind = activeBranches(end:-1:1) tr.activeNodes(tr.tree(ind,:))=tr.activeNodes(ind+tr.numLeaves); end tr.lastThresholdValue = get(tr.slider,'Value'); set([tr.slider,tr.slidertx,tr.sliderok],'Visible','off') tr.selected(:) = false; tr.sel2root = path2root(tr,tr.selected); set(gcbf,'userdata',tr) changeEditMode(h); updateTree(gcbf,[],[]); autoFit(h) enableAllUI(h) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function cancelThresholdCut(h,varargin) %#ok % this helper function cancels the threshold cut mode and returns to the % 'select' mode. Entry point: the ways to get into this function is % by the 'CANCEL' uicontrol next to the slider (does not exist yet) or by % mouse click over the axes diring the slider mode. tr = get(gcbf,'userdata'); set([tr.slider,tr.slidertx,tr.sliderok],'Visible','off') changeEditMode(h); updateTree(gcbf,[],[]); enableAllUI(h) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function expandAll(h,varargin) %#ok % Callback function to expand all hidden nodes tr = get(gcbf,'userdata'); x=[0 inf]; [dump,anchor]=min(abs((mean(ylim)-tr.y))+x(1+tr.activeNodes)'); %#ok tr.activeBranches(:) = true; tr.activeNodes(:) = true; set(gcbf,'Userdata',tr); updateTree(gcbf,[],anchor) autoFit(h) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Callback function to save tree function saveNewick(h,varargin) %#ok tr = get(gcbf,'userdata'); newtr.tree = tr.tree; newtr.dist = tr.dist; newtr.names = tr.names; phytreewrite(phytree(newtr),'GUI',true); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Callback function to restore the original tree function restoreTree(h,varargin) %#ok tr = getappdata(gcbf,'backupTree'); view(phytree(tr)) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Callback function to load tree function loadNewick(h,varargin) %#ok if strcmp(get(h,'Type'),'uimenu') % if caller is the uimenu then needs figtoclose = gcbf; tr = phytreeread; % to pick a file else % if not, caller is the callback from get workspace var tr=[]; pfig = get(h,'Parent'); if strcmp(get(h,'string'),'Import') || ... (strcmp(get(h,'style'),'listbox') && strcmp(get(gcbf,'SelectionType'),'open')) hp = get(pfig,'Userdata'); figtoclose = hp(4); ops = get(hp(1),'string'); if ~isempty(ops) tr = evalin('base',ops(get(hp(1),'value'),:)); end close(pfig); elseif strcmp(get(h,'string'),'Cancel') close(pfig); end end if ~isempty(tr) propsForFigure = getappdata(figtoclose,'propsForFigure'); view(tr,[],propsForFigure); close(figtoclose) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Callback function to close tree function closeNewick(h,varargin) %#ok close(gcbf) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Callback function to copy the tree to a figure function doPublishFigure(h,varargin,AllNodes,callerIsContextMenu) %#ok pfig = get(h,'Parent'); if strcmp(get(h,'string'),'Cancel') close(pfig); return; end % get options from the publishdlg window and close it hp = get(pfig,'Userdata'); vfig = hp(7); va = get(hp(1:6),'Value'); va = [va{:}]; switch find(va(1:3)) case 1; args = {'type','square'}; case 2; args = {'type','angular'}; case 3; args = {'type','radial'}; end args = {args{:},'bra',va(4),'lea',va(5),'ter',va(6)}; close(pfig); % now select the branch to publish based on selected points tr = get(vfig,'userdata'); if ~callerIsContextMenu & ~tr.selected %#ok % if called from the uimenu and tr.selected(end) = true; % nothing is selected pick the root end selected = find(tr.selected); commonpath = true(tr.numLabels,1); for ind = 1:numel(selected) commonpath = commonpath & path2root(tr,selected(ind)); end branchtoexp = find(commonpath,1); tr.selected(:) = false; tr.selected(branchtoexp) = true; set(vfig,'userdata',tr) updateTree(vfig,[],[]); hp = branchtoexp; %find all nodes to export (i.e. all descendants) children = false(1,tr.numLabels); children(hp) = true; if AllNodes for ind = hp-tr.numLeaves:-1:1 if children(ind+tr.numLeaves) children(tr.tree(ind,:))=true; end end permuta = find(children); else for ind = hp-tr.numLeaves:-1:1 if children(ind+tr.numLeaves) children(tr.tree(ind,:))=tr.activeNodes(tr.tree(ind,:)); end end braToLea = find(~tr.activeNodes(tr.tree(:,1)))+tr.numLeaves; expBran = find(children(tr.numLeaves+1:end)) + tr.numLeaves; permuta = [find(children(1:tr.numLeaves)) ... intersect(expBran,braToLea)]; [dump,hs] = sort(tr.y(permuta)); %#ok permuta = [permuta(hs) setdiff(expBran,braToLea)]; end if sum(children)>1 % enough leaves to export ? newtr = phytree; ipermuta(permuta) = 1:length(permuta); numLeaves = (ipermuta(end) + 1)/2; newtr.tree = ipermuta(tr.tree(permuta(numLeaves+1:end)-tr.numLeaves,:)); newtr.dist = tr.dist(permuta); newtr.names = tr.names(permuta); plot(newtr,true(length(newtr.tree),1),args{:}) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Callback function for autofit function autoFit(h,varargin) %#ok tr = get(gcbf,'userdata'); set(tr.ha,'Ylim',[min(tr.y(tr.activeNodes))-1,max(tr.y(tr.activeNodes))+1]); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Callback function to Reset view function myResetView(h,varargin) %#ok tr = get(gcbf,'userdata'); set(tr.ha,'Ylim',tr.yLim); set(tr.ha,'Xlim',tr.xLim); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Common export function function exportSubtree(h,varargin,AllNodes,ToWS,callerIsContextMenu) %#ok tr = get(gcbf,'userdata'); % if called from the uimenu and nothing is selected pick the root if ~callerIsContextMenu & ~tr.selected %#ok tr.selected(end) = true; end selected = find(tr.selected); commonpath = true(tr.numLabels,1); for ind = 1:numel(selected) commonpath = commonpath & path2root(tr,selected(ind)); end branchtoexp = find(commonpath,1); tr.selected(:) = false; tr.selected(branchtoexp) = true; set(gcbf,'userdata',tr) updateTree(gcbf,[],[]); doExport(branchtoexp,AllNodes,ToWS) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Common export function after having selected a point function doExport(hp,AllNodes,ToWS) tr = get(gcbf,'userdata'); %find all nodes to export (i.e. all descendants) children = false(1,tr.numLabels); children(hp) = true; if AllNodes for ind = hp-tr.numLeaves:-1:1 if children(ind+tr.numLeaves) children(tr.tree(ind,:))=true; end end permuta = find(children); else for ind = hp-tr.numLeaves:-1:1 if children(ind+tr.numLeaves) children(tr.tree(ind,:))=tr.activeNodes(tr.tree(ind,:)); end end braToLea = find(~tr.activeNodes(tr.tree(:,1)))+tr.numLeaves; expBran = find(children(tr.numLeaves+1:end)) + tr.numLeaves; permuta = [find(children(1:tr.numLeaves)) ... intersect(expBran,braToLea)]; [dump,hs] = sort(tr.y(permuta)); %#ok permuta = [permuta(hs) setdiff(expBran,braToLea)]; end if sum(children)>1 % enough leaves to export ? newtr=phytree; ipermuta(permuta) = 1:length(permuta); numLeaves = (ipermuta(end) + 1)/2; newtr.tree = ipermuta(tr.tree(permuta(numLeaves+1:end)-tr.numLeaves,:)); newtr.dist = tr.dist(permuta); newtr.names = tr.names(permuta); if ToWS % export to workspace ? s = inputdlg('Workspace variable name ?','Export to Workspace',1); while ~(isempty(s) || isvarname(s{1}) || isempty(s{1})) s = inputdlg('Not a valid variable name, type a MATLAB variable name ?','Export to Workspace',1); end if ~(isempty(s) || isempty(s{1})) assignin('base',s{1},newtr) end else % no, then export to other viewer view(newtr); end end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function updateTree(h,highLight,anchor) %#ok % Redraws the tree depending on the active Branches % rather than erase and redraw, we only change specific fields in hlines % and hdots. tr = get(h,'userdata'); activeBranches=find(tr.activeBranches)'; oldPos = tr.y(anchor); % propagate last leaf lastleaf = 1:tr.numLabels; for ind = tr.numBranches:-1:1 if ~tr.activeNodes(tr.tree(ind,1)) lastleaf(tr.tree(ind,:))=lastleaf(ind+tr.numLeaves); end end % find x coordinates of branches tr.x = tr.dist; for ind = tr.numBranches:-1:1 tr.x(tr.tree(ind,:)) = tr.x(tr.tree(ind,:)) + tr.x(ind+tr.numLeaves); end % find y coordinates of branches dummy = lastleaf([true,diff(lastleaf(1:tr.numLeaves))~=0]); tr.y=zeros(tr.numLabels,1); tr.y(dummy)=1:length(dummy); for ind = activeBranches tr.y(ind+tr.numLeaves) = mean(tr.y(tr.tree(ind,:))); end % update right labels todis = tr.names(dummy); set(tr.ha,'ytick',1:length(dummy),'yticklabel',todis) % show only active branches set(tr.hlines,'Visible','off') set(tr.hlines(tr.activeNodes),'Visible','on') % update coordinates in lines for ind = 1:tr.numLabels-1 set(tr.hlines(ind),'Ydata',tr.y([ind,ind,tr.par(ind)])) set(tr.hlines(ind),'Xdata',tr.x([ind,tr.par([ind ind])])) end set(tr.hlines(tr.numLabels),'Ydata',tr.y(tr.numLabels)*[1 1 1]) set(tr.hlines(tr.numLabels),'Xdata',tr.x(tr.numLabels)*[1 1 1]) % update dots mask = false(tr.numLabels,1); mask(1:tr.numLeaves) = true; set(tr.hdots(1),'Ydata',tr.y(tr.activeNodes&~mask),'Xdata',tr.x(tr.activeNodes&~mask)) set(tr.hdots(2),'Ydata',tr.y(tr.activeNodes&mask),'Xdata',tr.x(tr.activeNodes&mask)) % update red dots set(tr.hseldots(1),'Ydata',tr.y(tr.activeNodes&~mask&tr.selected),'Xdata',tr.x(tr.activeNodes&~mask&tr.selected)) set(tr.hseldots(2),'Ydata',tr.y(tr.activeNodes&mask&tr.selected),'Xdata',tr.x(tr.activeNodes&mask&tr.selected)) % set the axis holders set(tr.axhold,'Ydata',[0.5,max(tr.y(tr.activeNodes))+0.5]) if numel(oldPos) set(tr.ha,'ylim',get(tr.ha,'ylim')+tr.y(anchor)-oldPos); else set(tr.ha,'ylim',get(tr.ha,'ylim')) % just touch 'YLim' such that the listener is triggered end % turn on indicative modes tr.indicativeMode = false; set([tr.datatip tr.hpathline],'visible','off') set(tr.hlines,'color','black') set(tr.hldots(1),'Xdata',[],'Ydata',[]) set(tr.hldots(2),'Xdata',[],'Ydata',[]) % save figure data set(h,'Userdata',tr) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function path2r = path2root(tr,from) % helper function, finds path to root path2r = false(tr.numLabels,1); if (numel(from)~=1 && sum(from)~=1) return; end path2r(from) = true; temp = find(path2r); if numel(temp) while temp~=tr.numLabels; temp = tr.par(temp); path2r(temp) = true; end end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [pathA,pathB] = path2sel(tr,from) % helper function, finds path to selected node path2rt = path2root(tr,from); commonPath = tr.sel2root & path2rt; commonPath(find(commonPath,1)) = false; pathB = tr.sel2root & ~commonPath; pathA = path2rt & ~commonPath; %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function tr = doBasicCalculations(tr) % helper function to compute and find some features of the tree tr = struct(tr); tr.numBranches = size(tr.tree,1); tr.numLeaves = tr.numBranches + 1; tr.numLabels = tr.numBranches + tr.numLeaves; % obtain parents for every node tr.par(tr.tree(:)) = tr.numLeaves + [1:tr.numBranches 1:tr.numBranches]; % calculate the distance to the closest leaf for every node % needed for fast threshold cut tr.dist2Leaf = zeros(tr.numLabels,1); for ind = 1:tr.numBranches tr.dist2Leaf(ind+tr.numLeaves) = ... min(tr.dist2Leaf(tr.tree(ind,:))+tr.dist(tr.tree(ind,:))); end % calculate drawing coordinates for the tree: x coordinated will never % change, but y coordinates may change depending on the active branches and % nodes. tr.x = tr.dist; tr.y=[1:tr.numLeaves zeros(1,tr.numBranches)]'; for ind = tr.numBranches:-1:1 tr.x(tr.tree(ind,:)) = tr.x(tr.tree(ind,:)) + tr.x(ind+tr.numLeaves); end for ind =1:tr.numBranches tr.y(ind+tr.numLeaves) = mean(tr.y(tr.tree(ind,:))); end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function correctFigureSize(fig,recommendedHeight) % helper function to increase initial figure size depending on the screen & % tree sizes screenSize = diff(reshape(get(0,'ScreenSize'),2,2),[],2)-100; % 100 gives extra space for the figure header and win toolbar position = get(fig,'Position'); if recommendedHeight > position(4) if recommendedHeight < sum(position([2 4])) position(2) = sum(position([2 4])) - recommendedHeight; position(4) = recommendedHeight; elseif recommendedHeight < screenSize(2) position(2) = 30; position(4) = recommendedHeight; else position(2) = 30; position(4) = screenSize(2); end set(fig,'Position',position) end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function hToggleToolbar = makePhyTreeViewerToolbar(fig) % helper function to set the toolbar % % hToggleToolbar contains handles to easy change the state on/off when % changing modes oldSH = get(0,'ShowHiddenHandles'); set(0,'ShowHiddenHandles','on') set(fig,'toolbar','figure') % needs to update because uicontrols turn it off % Fix toolbar options, we keep: ZoomIn,ZoomOut,Pan hw = findall(fig,'type','uitoolbar'); hf = get(hw,'Children'); h1 = findall(hf,'Tag','Exploration.Pan'); h2 = findall(hf,'Tag','Exploration.ZoomOut'); h3 = findall(hf,'Tag','Exploration.ZoomIn'); delete(setxor(hf,[h1,h2,h3])) set([h1 h2 h3],'Separator','off','clickedCallback',@changeEditMode); % load icons load(fullfile(matlabroot,'toolbox','bioinfo','bioinfo','@phytree','phytreeicons')) h4 = uitoggletool('ToolTip','Inspect Tool Mode','separator','on',... 'Tag','Inspect', 'CData',icons(:,:,1:3)); %#ok h5 = uitoggletool('ToolTip','Collapse/Expand Branch Mode',... 'Tag','Collapse/Expand','CData',icons(:,:,4:6)); %#ok h6 = uitoggletool('ToolTip','Rotate Branch Mode',... 'Tag','Rotate Branch', 'CData',icons(:,:,7:9)); %#ok h7 = uitoggletool('ToolTip','Rename Leaf/Branch Mode',... 'Tag','Rename', 'CData',icons(:,:,10:12)); %#ok h8 = uitoggletool('ToolTip','Prune (delete) Leaf/Branch Mode',... 'Tag','Prune', 'CData',icons(:,:,13:15)); %#ok set([h4 h5 h6 h7 h8],'clickedCallback',@changeEditMode,'state','off',... 'Serializable','off','HandleVisibility','off'); hToggleToolbar = [h4 h5 h6 h7 h8 h1 h2 h3]; set(0,'ShowHiddenHandles',oldSH) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function hToggleUIMenu = makePhyTreeViewerUIMenus(fig) % helper function to set UI menus % % hToggleUIMenu contains handles to easy check on/off modes in the UI menu oldSH = get(0,'ShowHiddenHandles'); set(0,'ShowHiddenHandles','on') % delete figure menus not used h1 = findall(fig,'Type','uimenu', 'Label','&Edit'); h2 = findall(fig,'Type','uimenu', 'Label','&View'); h3 = findall(fig,'Type','uimenu', 'Label','&Insert'); h4 = findall(fig,'Type','uimenu', 'Label','&Desktop'); delete([h1,h2,h3,h4]) % Repair "File" menu hw = findall(fig,'Type','uimenu', 'Label','&File'); hf = get(hw,'children'); h1 = findall(hw,'Label','Expo&rt Setup...'); h3 = findall(hw,'Label','Print Pre&view...'); h4 = findall(hw,'Label','&Print...'); delete(setxor(hf,[h1,h3,h4])) uimenu(hw,'Label','New Tool...', 'Position',1,'Callback','phytreetool') uimenu(hw,'Label','Open...', 'Position',2,'Callback',@loadNewick) uimenu(hw,'Label','Import from Workspace...','Position',3,'Callback',@importfromwsdlg) uimenu(hw,'Label','Open Original in New Tool','Position',4,'Callback',@restoreTree) uimenu(hw,'Label','Save As...', 'Position',5,'Callback',@saveNewick,'Separator','on') item0 = uimenu(hw ,'Label','Print to Figure','Position',6); uimenu(item0,'Label','With Hidden Nodes...','Callback',{@publishdlg,1,0}); uimenu(item0,'Label','Only Displayed...', 'Callback',{@publishdlg,0,0}); item1 = uimenu(hw,'Label','Export to New Tool','Position',7); uimenu(item1,'Label','With Hidden Nodes...','Callback',{@exportSubtree,1,0,0}); uimenu(item1,'Label','Only Displayed...', 'Callback',{@exportSubtree,0,0,0}); item2 = uimenu(hw,'Label','Export to Workspace','Position',8); uimenu(item2,'Label','With Hidden Nodes...','Callback',{@exportSubtree,1,1,0}); uimenu(item2,'Label','Only Displayed...', 'Callback',{@exportSubtree,0,1,0}); uimenu(hw,'Label','Exit','Separator','on','Position',12,'Callback',@closeNewick) set(h1,'Separator','on') % Repair "Tools" menu hw = findall(fig,'Type','uimenu','Label','&Tools'); hf = get(hw,'children'); h1 = findall(hw,'Tag','figMenuZoomIn'); set(h1,'Callback',{@changeEditMode,gcbo}); h2 = findall(hw,'Tag','figMenuZoomOut'); set(h2,'Callback',{@changeEditMode,gcbo}); h3 = findall(hw,'Tag','figMenuPan'); set(h3,'Callback',{@changeEditMode,gcbo}); h4 = findall(hw,'Tag','figMenuResetView'); set(h4,'Callback',{@myResetView,gcbo}); h5 = findall(hw,'Tag','figMenuOptions'); set([h1,h4],'separator','off') delete(setxor(hf,[h1,h2,h3,h4,h5])) delete(findall(h5,'Tag','figMenuOptionsDatatip')) delete(findall(h5,'Tag','figMenuOptionsDataBar')) h6 = uimenu(hw,'Label','Inspect','Position',1,'Callback',@changeEditMode); h7 = uimenu(hw,'Label','Collapse/Expand','Position',2,... 'Callback',@changeEditMode); h8 = uimenu(hw,'Label','Rotate Branch','Position',3,... 'Callback',@changeEditMode); h9 = uimenu(hw,'Label','Rename','Position',4, 'Callback',@changeEditMode); h10 = uimenu(hw,'Label','Prune','Position',5, 'Callback',@changeEditMode); item3 = uimenu(hw,'Label','Threshold Collapse','Position',9,'Separator','on'); uimenu(item3,'Label','Distance to Leaves', 'Callback',@thresholdCut); uimenu(item3,'Label','Distance to Root', 'Callback',@thresholdCut); uimenu(hw,'Label','Expand All','Position',10,'Callback',@expandAll); uimenu(hw,'Label','Find Leaf/Branch...','Position',11,'Callback',@findNode); uimenu(hw,'Label','Fit to Window','Position',12,'Separator','on',... 'Callback',@autoFit); set(h1,'Separator','on') % Repair "Help" menu hw = findall(fig,'Type','uimenu','Label','&Help'); delete(get(hw,'children')); uimenu(hw,'Label','Bioinformatics Toolbox Help','Position',1,'Callback',... 'web([docroot ''/toolbox/bioinfo/bioinfo_product_page.html''])') uimenu(hw,'Label','Phylogenetic Tree Tool Help','Position',2,'Callback',... ['helpview(fullfile(docroot,''toolbox'',''bioinfo'', ''bioinfo.map'')' ... ',''phytreetool_reference'')' ] ) uimenu(hw,'Label','Demos','Position',3,'Separator','on',... 'Callback','demo(''toolbox'',''bioinfo'')') tlbx = ver('bioinfo'); mailstr = ['web(''mailto:bioinfofeedback@mathworks.com?subject=',... 'Feedback%20for%20Phytreetool%20in%20Bioinformatics',... '%20Toolbox%20',tlbx(1).Version,''')']; uimenu(hw,'Label','Send Feedback','Position',4,'Separator','on',... 'Callback',mailstr); set(0,'ShowHiddenHandles',oldSH) hToggleUIMenu = [h6 h7 h8 h9 h10 h1 h2 h3]; %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [hToggleContextMenu,hAxisContextMenu,hDotsContextMenu] = ... makePhyTreeViewerContextMenus(fig) %#ok % helper function to set context menus % % hToggleContextMenu contains handles to easy check on/off modes in the % Context Menu % hAxisContextMenu contains handle to the Axis Context Menu to used to % reactivate it % hDotsContextMenu contains handle to the Dots Context Menu to used to % reactivate it % set up context menu for the axes (when mouse is not over a node) hcm1 = uicontextmenu('Callback',@hideActiveIndicators); h1 = uimenu(hcm1,'Label','Inspect', 'Callback',@changeEditMode); h2 = uimenu(hcm1,'Label','Collapse/Expand','Callback',@changeEditMode); h3 = uimenu(hcm1,'Label','Rotate Branch', 'Callback',@changeEditMode); h4 = uimenu(hcm1,'Label','Rename', 'Callback',@changeEditMode); h5 = uimenu(hcm1,'Label','Prune', 'Callback',@changeEditMode); item4 = uimenu(hcm1 , 'Label', 'Threshold Collapse','Separator','on'); uimenu(item4,'Label','Distance to Leaves', 'Callback',@thresholdCut); uimenu(item4,'Label','Distance to Root', 'Callback',@thresholdCut); uimenu(hcm1,'Label','Expand All','Callback',@expandAll); uimenu(hcm1 ,'Label','Find Leaf/Branch...', 'Callback',@findNode); uimenu(hcm1 ,'Label','Fit to Window','Separator','on','Callback',@autoFit); uimenu(hcm1 ,'Label','Reset to Original View', 'Callback',@myResetView); % context menu for dots (when mouse over a dot and right mouse button pressed) hcm2 = uicontextmenu('Callback',@enableOptionsContextMenu); uimenu(hcm2,'Label','Collapse/Expand', 'Callback',@collapseExpand); uimenu(hcm2,'Label','Rotate Branch', 'Callback',@rotateBranch); uimenu(hcm2,'Label','Rename', 'Callback',@renameNode); uimenu(hcm2,'Label','Prune', 'Callback',@pruneTree); item0 = uimenu(hcm2,'Label','Print to Figure','Separator','on'); uimenu(item0,'Label','With Hidden Nodes...','Callback',{@publishdlg,1,1}); uimenu(item0,'Label','Only Displayed...', 'Callback',{@publishdlg,0,1}); item1 = uimenu(hcm2,'Label','Export to New Tool'); uimenu(item1,'Label','With Hidden Nodes...','Callback',{@exportSubtree,1,0,1}); uimenu(item1,'Label','Only Displayed...', 'Callback',{@exportSubtree,0,0,1}); item2 = uimenu(hcm2,'Label','Export to Workspace'); uimenu(item2,'Label','With Hidden Nodes...','Callback',{@exportSubtree,1,1,1}); uimenu(item2,'Label','Only Displayed...', 'Callback',{@exportSubtree,0,1,1}); %uimenu(hcm2,'Label','Color Down', 'Callback',@colorDown); % save context menus in my data structure to later restore them if desired hToggleContextMenu = [h1 h2 h3 h4 h5]; hAxisContextMenu = hcm1; hDotsContextMenu = hcm2; %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function enableOptionsContextMenu(h,varargin) hideActiveIndicators selectNode(h,varargin) tr = get(gcbf,'Userdata'); hc = get(tr.hDotsContextMenu,'Children'); set(hc,'Enable','on') if sum(tr.selected)~=1 set(hc(5),'Enable','off'); end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function setupYLabelsListeners % helper function to setsup listeners for the ylables, so we can detect if % we would need to change the fontsize hgp = findpackage('hg'); axesC = findclass(hgp,'axes'); figureC = findclass(hgp,'figure'); % listens when the Ylim of axes has changed YLimListener = handle.listener(gca,axesC.findprop('YLim'),... 'PropertyPostSet',{@ylabelsListener,gcf,gca}); % listens when Position of Figure has changed PositionListener = handle.listener(gcf,figureC.findprop('Position'),... 'PropertyPostSet',{@ylabelsListener,gcf,gca}); % store the listeners setappdata(gcf,'PhyTreeListeners',[YLimListener, PositionListener]); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function publishdlg(h,varargin,AllNodes,callerIsContextMenu) %#ok % dialog window to select options for publishing vfig = gcbf; c = get(0,'ScreenSize')*[1 0;0 1;.5 0;0 .5]; fig = figure('WindowStyle','modal','Color',[0.831373 0.815686 0.784314],... 'Position',[c-[150 100] 300 200],'Resize','off','NumberTitle','off',... 'Name','Print Phylogenetic Tree to Figure','IntegerHandle','off' ); h1=uibuttongroup;h2=uibuttongroup; set(h1,'Position',[.08 .35 .35 .55],'Title','Rendering Type','backgroundcolor',[0.831373 0.815686 0.784314]) set(h2,'Position',[.52 .35 .39 .55],'Title','Display Labels','backgroundcolor',[0.831373 0.815686 0.784314]) ui1=uicontrol(h1,'style','radiobutton','Position',[5 70 90 20],'string','Square','value',1,'backgroundcolor',[0.831373 0.815686 0.784314]); ui2=uicontrol(h1,'style','radiobutton','Position',[5 40 90 20],'string','Angular','backgroundcolor',[0.831373 0.815686 0.784314]); ui3=uicontrol(h1,'style','radiobutton','Position',[5 10 90 20],'string','Radial','backgroundcolor',[0.831373 0.815686 0.784314]); ui4=uicontrol(h2,'style','checkbox','Position',[5 70 109 20],'string','Branch Nodes','backgroundcolor',[0.831373 0.815686 0.784314]); ui5=uicontrol(h2,'style','checkbox','Position',[5 40 109 20],'string','Leaf Nodes','backgroundcolor',[0.831373 0.815686 0.784314]); ui6=uicontrol(h2,'style','checkbox','Position',[5 10 109 20],'string','Terminal Nodes','value',1,'backgroundcolor',[0.831373 0.815686 0.784314]); uicontrol(fig,'style','pushbutton','Position',[70 20 60 30],'string','Print','Callback',{@doPublishFigure,AllNodes,callerIsContextMenu}); uicontrol(fig,'style','pushbutton','Position',[155 20 60 30],'string','Cancel','Callback',{@doPublishFigure,AllNodes,callerIsContextMenu}); set(fig,'Userdata',[ui1 ui2 ui3 ui4 ui5 ui6 vfig]); set(h1,'SelectionChangeFcn',{@toggleCheckBoxs,ui3,ui6}) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function toggleCheckBoxs(h,event,h3,h6) %#ok if get(h3,'value') set(h6,'enable','off') else set(h6,'enable','on') end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function importfromwsdlg(h,varargin) %#ok % dialog window to select variable from workspace mvars = evalin('base','whos'); mvars = mvars(strmatch('phytree',{mvars(:).class})); %#ok vfig = gcbf; c = get(0,'ScreenSize')*[1 0;0 1;.5 0;0 .5]; fig = figure('WindowStyle','modal','Color',[0.831373 0.815686 0.784314],... 'Position',[c-[80 100] 160 220],'Resize','off','NumberTitle','off',... 'Name','Get Phytree Object','IntegerHandle','off' ); ui1=uicontrol(fig,'style','list','Position',[22 70 120 120],'string',char(mvars(:).name),'backgroundcolor','w','Callback',@loadNewick); ui2=uicontrol(fig,'style','pushbutton','Position',[15 20 60 30],'string','Import','Callback',@loadNewick); ui3=uicontrol(fig,'style','pushbutton','Position',[90 20 60 30],'string','Cancel','Callback',@loadNewick); uicontrol(fig,'style','text','Position',[20 190 140 20],'string','Select phytree object:','Horizontal','left','backgroundcolor',[0.831373 0.815686 0.784314]) set(fig,'Userdata',[ui1 ui2 ui3 vfig]); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function n = getphytreetoolnumber() % Computes the index number for this particular tool % first, finds the used numbers so far allFigs = findall(0,'tag','PhyTreeTool'); usedNumbers = zeros(1,numel(allFigs)+1); baseName = 'Phylogenetic Tree Tool '; baseLen = length(baseName); for i = 1:numel(allFigs) str = get(allFigs(i),'Name'); usedNumbers(i) = str2double(str(baseLen:end)); end % This is how we find the next index. The rule is that we find the lowest % integer value (non-zero and positive) not yet prescribed to a phytree % tool, This is the same way MATLAB figures behave. n = num2str(min(setdiff(1:(max(usedNumbers)+1),usedNumbers))); %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function n = getacceptedwarningfromothertools(cfig) %#ok % Finds out if the pruning warning has been accepted % first, finds the used numbers so far allFigs = findall(0,'tag','PhyTreeTool'); n = 'NotDone'; for i = 1:numel(allFigs) propsForFigure = getappdata(allFigs(i),'propsForFigure'); if isequal(propsForFigure.PruneWarning,'Done') n = 'Done'; end end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function setacceptedwarningtoothertools() % Set the pruning warning to all the other open tools as 'Done' allFigs = findall(0,'tag','PhyTreeTool'); for i = 1:numel(allFigs) propsForFigure = getappdata(allFigs(i),'propsForFigure'); propsForFigure.PruneWarning = 'Done'; setappdata(allFigs(i),'propsForFigure',propsForFigure); end