annotate toolboxes/graph_visualisation/graphViz4Matlab/graphViz4Matlab.m @ 0:e9a9cd732c1e tip

first hg version after svn
author wolffd
date Tue, 10 Feb 2015 15:05:51 +0000
parents
children
rev   line source
wolffd@0 1 classdef graphViz4Matlab < handle
wolffd@0 2 % Visualize a graph in a Matlab figure window by specifying an
wolffd@0 3 % adjacency matrix and optionally node labels, descriptions, colors and the
wolffd@0 4 % the layout algorithm. The best layout algorithms require that graphViz be
wolffd@0 5 % installed, available free at <http://www.graphviz.org>.
wolffd@0 6 %
wolffd@0 7 % Matthew Dunham
wolffd@0 8 % University of British Columbia
wolffd@0 9 % Last Updated: April 24, 2010
wolffd@0 10 % Requires Matlab version 2008a or newer.
wolffd@0 11 %
wolffd@0 12 % Syntax (see more examples below):
wolffd@0 13 % graphViz4Matlab('-adjMat',adj,'-nodeLabels',labels,'-nodeColors',colors);
wolffd@0 14 %
wolffd@0 15 % Self loops are removed and not represented on the graph.
wolffd@0 16 %
wolffd@0 17 % Once the graph is displayed, there are several operations you can perform
wolffd@0 18 % with the mouse.
wolffd@0 19 % (1) Move a single node around.
wolffd@0 20 % (2) Draw a mouse box around several nodes and move them all together.
wolffd@0 21 % (3) Enter a description for a node by double clicking it.
wolffd@0 22 % (4) Shade the node by right clicking it
wolffd@0 23 % (5) Display a node's properties on the console by shift clicking it.
wolffd@0 24 % (6) Increase or decrease the font size
wolffd@0 25 % (7) Increase or decrease the node size
wolffd@0 26 % (8) Tighten the axes and relax the square aspect ratio.
wolffd@0 27 % (9) Ask the current layout algorithm to layout the nodes as though the
wolffd@0 28 % arrows were pointing the other way. This only affects some of the
wolffd@0 29 % layouts.
wolffd@0 30 % (10) Change the layout algorithm and refresh the graph
wolffd@0 31 %
wolffd@0 32 % Additionally, any operation you could do with a regular Matlab figure can
wolffd@0 33 % be done here, e.g. annotating or saving as a pdf.
wolffd@0 34 %
wolffd@0 35 % Options are specified via name value pairs in any order.
wolffd@0 36 % [] denote defaults.
wolffd@0 37 %
wolffd@0 38 % '-adjMat' [example matrix] The adjacency matrix
wolffd@0 39 %
wolffd@0 40 % '-layout' [Gvizlayout if graphViz installed, else Gridlayout]
wolffd@0 41 % A layout object, i.e. Gvizlayout | Gridlayout | Circlelayout
wolffd@0 42 % (See knownLayouts Property)
wolffd@0 43 %
wolffd@0 44 % '-nodeLabels' ['1':'n'] A cell array of labels for the nodes
wolffd@0 45 %
wolffd@0 46 % '-nodeDescriptions' [{}] Longer descriptions for the nodes, displayed when
wolffd@0 47 % double clicking on a node.
wolffd@0 48 %
wolffd@0 49 % '-nodeColors' ['c'] A cell array or n-by-3 matrix specifying colors
wolffd@0 50 % for the nodes. If fewer colors than nodes are specified,
wolffd@0 51 % the specified colors are reused and cycled through.
wolffd@0 52 %
wolffd@0 53 % '-undirected' [false] If true, no arrows are displayed.
wolffd@0 54 %
wolffd@0 55 % '-edgeColors' [] An n-by-3 cell array listing
wolffd@0 56 % {fromName,toName,color} for each row. You can
wolffd@0 57 % list only the n < numel(edges) edges you want to
wolffd@0 58 % color. If you do not label the nodes, graphViz4Matlab
wolffd@0 59 % uses '1','2','3', etc, in which case use these.
wolffd@0 60 % You can specify the text 'all' in place of toName,
wolffd@0 61 % to mean all nodes, i.e. {fromName,'all',color}
wolffd@0 62 %
wolffd@0 63 %
wolffd@0 64 % '-splitLabels' [true] If true, long node labels are split into
wolffd@0 65 % several rows
wolffd@0 66 %
wolffd@0 67 % '-doubleClickFn' (by default, double clicking a node brings up
wolffd@0 68 % an edit box for the node's description, but you
wolffd@0 69 % can pass in a custom function handle. The function
wolffd@0 70 % gets passed the node's label.
wolffd@0 71 % Examples:
wolffd@0 72 %
wolffd@0 73 % adj = rand(5,5) > 0.8;
wolffd@0 74 % labels = {'First','Second','Third','Fourth','Fifth'};
wolffd@0 75 % colors = {'g','b'}; % will cycle through
wolffd@0 76 % s = graphViz4Matlab('-adjMat',adj,'-nodeLabels',labels,'-nodeColors',colors);
wolffd@0 77 % freeze(s); % convert to an image
wolffd@0 78 %
wolffd@0 79 % If you are only specifying an adjacency matrix, you can omit the
wolffd@0 80 % '-adjMat' name as in graphViz4Matlab(adj).
wolffd@0 81 %
wolffd@0 82 % Calling graphViz4Matlab without any parameters displays an example graph.
wolffd@0 83 %
wolffd@0 84
wolffd@0 85
wolffd@0 86 properties(GetAccess = 'public', SetAccess = 'private')
wolffd@0 87 % read only
wolffd@0 88 path = addpath(genpath(fileparts(which(mfilename)))); % automatically adds subdirectories to path
wolffd@0 89 graphVizPath = setupPath();
wolffd@0 90 nnodes = 0; % The number of nodes
wolffd@0 91 nedges = 0; % The number of edges
wolffd@0 92 currentLayout= []; % The current layout object
wolffd@0 93 layouts = []; % List currently added layout objects
wolffd@0 94 adjMatrix = []; % The adjacency matrix
wolffd@0 95 isvisible = false; % True iff the graph is being displayed
wolffd@0 96 nodeArray = []; % The nodes
wolffd@0 97 edgeArray = []; % The edges
wolffd@0 98 fig = []; % The main window
wolffd@0 99 ax = []; % The main axes
wolffd@0 100 doubleClickFn = []; % function to execute when a user double clicks on a node, (must be a function handle that takes in the node name
wolffd@0 101 selectedNode = []; % The selected node, if any
wolffd@0 102 minNodeSize = []; % A minimum size for the nodes
wolffd@0 103 maxNodeSize = []; % A maximum size for the nodes
wolffd@0 104 undirected = false; % If undirected, arrows are not displayed
wolffd@0 105 flipped = false; % If true, layout is done as though edge directions were reversed.
wolffd@0 106 % (does not affect the logical layout).
wolffd@0 107 knownLayouts = {Gvizlayout ,... % add your own layout here or use
wolffd@0 108 Treelayout ,... % the addLayout() method
wolffd@0 109 Radiallayout,...
wolffd@0 110 Circularlayout,...
wolffd@0 111 Springlayout,...
wolffd@0 112 Circlelayout,...
wolffd@0 113 Gridlayout ,...
wolffd@0 114 Randlayout };
wolffd@0 115 defaultEdgeColor = [0,0,0];%[20,43,140]/255;
wolffd@0 116 edgeColors;
wolffd@0 117 square = true; % amounts to a the call "axis square"
wolffd@0 118 splitLabels = true;
wolffd@0 119 end
wolffd@0 120
wolffd@0 121 properties(GetAccess = 'private', SetAccess = 'private')
wolffd@0 122 % These values store the initial values not the current ones.
wolffd@0 123 nodeLabels = {};
wolffd@0 124 nodeDescriptions = {};
wolffd@0 125 nodeColors = {};
wolffd@0 126 end
wolffd@0 127
wolffd@0 128 properties(GetAccess = 'protected',SetAccess = 'protected')
wolffd@0 129 toolbar; % The button toolbar
wolffd@0 130 layoutButtons; % The layout buttons
wolffd@0 131 fontSize; % last calculated optimal font size
wolffd@0 132 selectedFontSize; %
wolffd@0 133
wolffd@0 134 previousMouseLocation; % last mouse location relative to the axes
wolffd@0 135 groupSelectionMode = 0; % current stage in a group selection task
wolffd@0 136 groupSelectedNodes; % selected nodes in a group selection
wolffd@0 137 groupSelectedDims; % size of enclosing rectangle of selected nodes
wolffd@0 138 groupSelectedRect; % a bounding rectangle for the selected nodes
wolffd@0 139 end
wolffd@0 140
wolffd@0 141 methods
wolffd@0 142
wolffd@0 143 function obj = graphViz4Matlab(varargin)
wolffd@0 144 % graphViz4Matlab constructor
wolffd@0 145 if(~exist('processArgs','file')), error('Requires processArgs() function'); end
wolffd@0 146 obj.addKnownLayouts();
wolffd@0 147 obj.processInputs(varargin{:})
wolffd@0 148 obj.addNodes();
wolffd@0 149 obj.addEdges();
wolffd@0 150 obj.draw();
wolffd@0 151 end
wolffd@0 152
wolffd@0 153 function draw(obj)
wolffd@0 154 % Draw the graph
wolffd@0 155 if(obj.isvisible)
wolffd@0 156 obj.erase()
wolffd@0 157 end
wolffd@0 158 obj.createWindow();
wolffd@0 159 obj.calculateMinMaxNodeSize();
wolffd@0 160 obj.layoutNodes();
wolffd@0 161 obj.displayGraph();
wolffd@0 162 obj.isvisible = true;
wolffd@0 163 obj.paperCrop();
wolffd@0 164 end
wolffd@0 165
wolffd@0 166 function fig = freeze(obj)
wolffd@0 167 % Freeze the current image into a regular Matlab figure
wolffd@0 168 figure(obj.fig);
wolffd@0 169 print tmp.png -dpng -r300
wolffd@0 170 fig = figure;
wolffd@0 171 image(imread('tmp.png'));
wolffd@0 172 axis off;
wolffd@0 173 delete tmp.png;
wolffd@0 174 close(obj.fig);
wolffd@0 175 end
wolffd@0 176
wolffd@0 177 function redraw(obj)
wolffd@0 178 % Redraw the graph. (You could also call draw() again but then the
wolffd@0 179 % window is recreated as well and it doesn't look as nice).
wolffd@0 180 if(~obj.isvisible)
wolffd@0 181 obj.draw();
wolffd@0 182 return;
wolffd@0 183 end
wolffd@0 184 cla(obj.ax);
wolffd@0 185 obj.clearGroupSelection();
wolffd@0 186 obj.calculateMinMaxNodeSize();
wolffd@0 187 obj.layoutNodes();
wolffd@0 188 obj.displayGraph();
wolffd@0 189 end
wolffd@0 190
wolffd@0 191 function flip(obj,varargin)
wolffd@0 192 % Have the layout algorithms layout the graph as though the arrows
wolffd@0 193 % were pointing in the opposite direction. The node connectivity
wolffd@0 194 % remains the same and if node one pointed to node 2 before, it
wolffd@0 195 % still does after. This is useful for tree layout, for example to
wolffd@0 196 % turn the tree on its head. Calling it twice flips it back.
wolffd@0 197 obj.flipped = ~obj.flipped;
wolffd@0 198 if(obj.isvisible)
wolffd@0 199 obj.redraw();
wolffd@0 200 end
wolffd@0 201 end
wolffd@0 202
wolffd@0 203 function erase(obj)
wolffd@0 204 % Erase the graph but maintain the state so that it can be redrawn.
wolffd@0 205 if(obj.isvisible)
wolffd@0 206 obj.clearGroupSelection();
wolffd@0 207 delete(obj.fig);
wolffd@0 208 obj.isvisible = false;
wolffd@0 209
wolffd@0 210 end
wolffd@0 211 end
wolffd@0 212
wolffd@0 213 function nodeSelected(obj,node)
wolffd@0 214 % This function is called by nodes when they are selected by the
wolffd@0 215 % mouse. It should not be called manually.
wolffd@0 216 if(obj.groupSelectionMode == 1)
wolffd@0 217 obj.groupSelectionStage1();
wolffd@0 218 return;
wolffd@0 219 end
wolffd@0 220 if(~isempty(obj.selectedNode))
wolffd@0 221 node.deselect();
wolffd@0 222 obj.selectedNode = [];
wolffd@0 223 return;
wolffd@0 224 end
wolffd@0 225 switch get(obj.fig,'SelectionType')
wolffd@0 226 case 'normal'
wolffd@0 227 obj.singleClick(node);
wolffd@0 228 case 'open'
wolffd@0 229 obj.doubleClick(node);
wolffd@0 230 case 'alt'
wolffd@0 231 obj.rightClick(node);
wolffd@0 232 otherwise
wolffd@0 233 obj.shiftClick(node);
wolffd@0 234 end
wolffd@0 235 end
wolffd@0 236
wolffd@0 237 function addLayout(obj,layout)
wolffd@0 238 % Let the graph know about a new layout you have created so that it
wolffd@0 239 % will be available via a toolbar button. The layout object must be
wolffd@0 240 % a descendant of the Abstractlayout class. This method does not have
wolffd@0 241 % to be called for existing layouts, nor does it need to be called
wolffd@0 242 % if you passed the new layout to the constructor or to the
wolffd@0 243 % setLayout() method. It will not add two layouts with the same
wolffd@0 244 % name property.
wolffd@0 245 if(~ismember(layout.name,fieldnames(obj.layouts)))
wolffd@0 246 if(layout.isavailable())
wolffd@0 247 obj.layouts.(layout.name) = layout;
wolffd@0 248 if(obj.isvisible)
wolffd@0 249 obj.addButtons();
wolffd@0 250 end
wolffd@0 251 else
wolffd@0 252 warning('graphViz4Matlab:layout','This layout is not available');
wolffd@0 253 end
wolffd@0 254 end
wolffd@0 255 end
wolffd@0 256
wolffd@0 257 function setLayout(obj,layout)
wolffd@0 258 % Set a new layout algorithm and refresh the graph.
wolffd@0 259 if(layout.isavailable())
wolffd@0 260 obj.addLayout(layout);
wolffd@0 261 obj.currentLayout = obj.layouts.(layout.name);
wolffd@0 262 obj.redraw();
wolffd@0 263 else
wolffd@0 264 warning('graphViz4Matlab:layout','Sorry, this layout is not available');
wolffd@0 265 end
wolffd@0 266 end
wolffd@0 267
wolffd@0 268 function squareAxes(obj,varargin)
wolffd@0 269 % Toggle the axes from square to normal and vice versa.
wolffd@0 270 obj.clearGroupSelection();
wolffd@0 271 if(obj.square)
wolffd@0 272 axis(obj.ax,'normal');
wolffd@0 273 obj.square = false;
wolffd@0 274 else
wolffd@0 275 axis(obj.ax,'square');
wolffd@0 276 obj.square = true;
wolffd@0 277 end
wolffd@0 278
wolffd@0 279 end
wolffd@0 280
wolffd@0 281 function tightenAxes(obj,varargin)
wolffd@0 282 % Tighten the axes as much as possible.
wolffd@0 283 obj.clearGroupSelection();
wolffd@0 284 xpos = vertcat(obj.nodeArray.xpos);
wolffd@0 285 ypos = vertcat(obj.nodeArray.ypos);
wolffd@0 286 r = obj.nodeArray(1).width/2;
wolffd@0 287 axis(obj.ax,[min(xpos)-r,max(xpos)+r,min(ypos)-r,max(ypos)+r]);
wolffd@0 288 axis normal;
wolffd@0 289 end
wolffd@0 290
wolffd@0 291
wolffd@0 292
wolffd@0 293
wolffd@0 294 end % end of public methods
wolffd@0 295
wolffd@0 296
wolffd@0 297 methods(Access = 'protected')
wolffd@0 298
wolffd@0 299 function addKnownLayouts(obj)
wolffd@0 300 % Add all of the known layouts
wolffd@0 301 obj.layouts = struct;
wolffd@0 302 for i=1:numel(obj.knownLayouts)
wolffd@0 303 layout = obj.knownLayouts{i};
wolffd@0 304 if(layout.isavailable())
wolffd@0 305 obj.layouts.(layout.name) = layout;
wolffd@0 306 end
wolffd@0 307 end
wolffd@0 308 end
wolffd@0 309
wolffd@0 310 function processInputs(obj,varargin)
wolffd@0 311 % Process the inputs and perform error checking
wolffd@0 312 labels = {'adj', 'adjMatrix', 'adjMat', 'layout', 'nodeLabels', 'nodeDescriptions', 'nodeColors', 'undirected', 'edgeColors', 'splitLabels', 'doubleClickFn'};
wolffd@0 313 for i=1:numel(varargin)
wolffd@0 314 arg = varargin{i};
wolffd@0 315 if ~ischar(arg), continue; end
wolffd@0 316 for j = 1:numel(labels)
wolffd@0 317 if strcmpi(arg, labels{i});
wolffd@0 318 varargin{i} = ['-', arg];
wolffd@0 319 end
wolffd@0 320 if strcmpi(arg, '-adj') || strcmpi(arg, '-adjMatrix')
wolffd@0 321 varargin{i} = '-adjMat';
wolffd@0 322 end
wolffd@0 323 end
wolffd@0 324 end
wolffd@0 325
wolffd@0 326 [adjMatrix, currentLayout, nodeLabels, nodeDescriptions, nodeColors,obj.undirected,obj.edgeColors,obj.splitLabels,obj.doubleClickFn] = processArgs(varargin,...
wolffd@0 327 '-adjMat' , [] ,...
wolffd@0 328 '-layout' , [] ,...
wolffd@0 329 '-nodeLabels' , {} ,...
wolffd@0 330 '-nodeDescriptions' , {} ,...
wolffd@0 331 '-nodeColors' , {} ,...
wolffd@0 332 '-undirected' , false ,...
wolffd@0 333 '-edgeColors' , [] ,...
wolffd@0 334 '-splitLabels' , true ,...
wolffd@0 335 '-doubleClickFn' , [] );
wolffd@0 336
wolffd@0 337
wolffd@0 338 if(~isempty(currentLayout) && ~isavailable(currentLayout))
wolffd@0 339 currentLayout = [];
wolffd@0 340 end
wolffd@0 341 if(isempty(adjMatrix))
wolffd@0 342 adjMatrix = [0 0 0 0; 1 0 0 0; 1 1 0 0; 1 1 1 0]; % example graph
wolffd@0 343 end
wolffd@0 344
wolffd@0 345 if(isempty(currentLayout))
wolffd@0 346 fields = fieldnames(obj.layouts);
wolffd@0 347 currentLayout = obj.layouts.(fields{1});
wolffd@0 348 else
wolffd@0 349 obj.addLayout(currentLayout);
wolffd@0 350 end
wolffd@0 351 obj.nnodes = size(adjMatrix,1);
wolffd@0 352 obj.adjMatrix = adjMatrix;
wolffd@0 353 if(isempty(nodeDescriptions))
wolffd@0 354 nodeDescriptions = repmat({'Enter a description here...'},size(adjMatrix,1),1);
wolffd@0 355 end
wolffd@0 356 obj.nodeDescriptions = nodeDescriptions;
wolffd@0 357 obj.nodeColors = nodeColors;
wolffd@0 358
wolffd@0 359 if(isempty(nodeLabels))
wolffd@0 360 nodeLabels = cellfun(@(x)num2str(x),mat2cell(1:obj.nnodes,1,ones(1,obj.nnodes)),'UniformOutput',false);
wolffd@0 361 end
wolffd@0 362
wolffd@0 363 obj.nodeLabels = nodeLabels;
wolffd@0 364 if(~isequal(numel(obj.nodeLabels),size(adjMatrix,1),size(adjMatrix,2)))
wolffd@0 365 error('graphViz4Matlab:dimMismatch','The number of labels must match the dimensions of adjmatrix.');
wolffd@0 366 end
wolffd@0 367 obj.currentLayout = currentLayout;
wolffd@0 368 end
wolffd@0 369
wolffd@0 370 function createWindow(obj)
wolffd@0 371 % Create the main window
wolffd@0 372 obj.fig = figure(floor(1000*rand) + 1000);
wolffd@0 373 set(obj.fig,'Name','graphViz4Matlab',...
wolffd@0 374 'NumberTitle' ,'off',...
wolffd@0 375 'Color','w' ,'Toolbar','none');
wolffd@0 376 obj.createAxes();
wolffd@0 377 ssize = get(0,'ScreenSize');
wolffd@0 378 pos = [ssize(3)/2,50,-20+ssize(3)/2,ssize(4)-200];
wolffd@0 379 set(obj.fig,'Position',pos);
wolffd@0 380 obj.setCallbacks();
wolffd@0 381 obj.addButtons();
wolffd@0 382
wolffd@0 383 end
wolffd@0 384
wolffd@0 385 function createAxes(obj)
wolffd@0 386 % Create the axes upon which the graph will be displayed.
wolffd@0 387 obj.ax = axes('Parent',obj.fig,'box','on','UserData','main');
wolffd@0 388 outerpos = get(obj.ax,'OuterPosition');
wolffd@0 389 axpos = outerpos;
wolffd@0 390 axpos(4) = 0.90;
wolffd@0 391 axpos(2) = 0.03;
wolffd@0 392 axis manual
wolffd@0 393 if(obj.square)
wolffd@0 394 axis square
wolffd@0 395 end
wolffd@0 396 set(obj.ax,'Position',axpos,'XTick',[],'YTick',[],'LineWidth',0.5);
wolffd@0 397 set(obj.ax,'ButtonDownFcn',@obj.axPressed);
wolffd@0 398 end
wolffd@0 399
wolffd@0 400
wolffd@0 401
wolffd@0 402 function setCallbacks(obj)
wolffd@0 403 % Set the callback functions for the figure, i.e. functions that
wolffd@0 404 % will be called when the user performs various actions.
wolffd@0 405 set(obj.fig,'ResizeFcn' ,@obj.windowResized);
wolffd@0 406 set(obj.fig,'WindowButtonMotionFcn' ,@obj.mouseMoved);
wolffd@0 407 set(obj.fig,'WindowButtonUpFcn' ,@obj.buttonUp);
wolffd@0 408 set(obj.fig,'DeleteFcn' ,@obj.deleted);
wolffd@0 409 end
wolffd@0 410
wolffd@0 411 function addNodes(obj)
wolffd@0 412 % Add all of the nodes to the graph structure, but don't display
wolffd@0 413 % them yet.
wolffd@0 414 obj.nodeArray = [];
wolffd@0 415 for i=1:obj.nnodes
wolffd@0 416 newnode = graphViz4MatlabNode(obj.nodeLabels{i});
wolffd@0 417 newnode.containingGraph = obj;
wolffd@0 418 newnode.showFullLabel = ~obj.splitLabels;
wolffd@0 419 obj.nodeArray = [obj.nodeArray newnode];
wolffd@0 420 end
wolffd@0 421 obj.addNodeDescriptions(obj.nodeDescriptions);
wolffd@0 422 obj.addNodeColors(obj.nodeColors);
wolffd@0 423 end
wolffd@0 424
wolffd@0 425 function addNodeDescriptions(obj,nodeDescriptions)
wolffd@0 426 % Add any descriptions to the newly created nodes.
wolffd@0 427 if(~isempty(nodeDescriptions))
wolffd@0 428 if(numel(nodeDescriptions) == 1)
wolffd@0 429 nodeDescriptions = repmat(nodeDescriptions,obj.nnodes,1);
wolffd@0 430 end
wolffd@0 431 for i=1:obj.nnodes
wolffd@0 432 obj.nodeArray(i).description = nodeDescriptions{i};
wolffd@0 433 end
wolffd@0 434 end
wolffd@0 435 end
wolffd@0 436
wolffd@0 437 function addNodeColors(obj,nodeColors)
wolffd@0 438 % Shade the nodes according to the specified colors. If too few
wolffd@0 439 % colors are specified, they are cycled through.
wolffd@0 440 if(~isempty(nodeColors))
wolffd@0 441 if(~iscell(nodeColors))
wolffd@0 442 nodeColors = mat2cell(nodeColors,ones(1,size(nodeColors,1)),size(nodeColors,2));
wolffd@0 443 end
wolffd@0 444 if(size(nodeColors,2) > size(nodeColors,1))
wolffd@0 445 nodeColors = nodeColors';
wolffd@0 446 end
wolffd@0 447 if(numel(nodeColors) < obj.nnodes)
wolffd@0 448 nodeColors = repmat(nodeColors,ceil(obj.nnodes/numel(nodeColors)),1);
wolffd@0 449 nodeColors = nodeColors(1:obj.nnodes);
wolffd@0 450 end
wolffd@0 451 for i=1:obj.nnodes
wolffd@0 452 obj.nodeArray(i).shade(nodeColors{i});
wolffd@0 453 end
wolffd@0 454 obj.nodeColors = nodeColors;
wolffd@0 455 end
wolffd@0 456 end
wolffd@0 457
wolffd@0 458 function addEdges(obj)
wolffd@0 459 % Add all of the edges to the graph structure, but don't display
wolffd@0 460 % them yet.
wolffd@0 461 if(any(diag(obj.adjMatrix)))
wolffd@0 462 fprintf('\nRemoving Self Loops\n');
wolffd@0 463 obj.adjMatrix = obj.adjMatrix - diag(diag(obj.adjMatrix));
wolffd@0 464 end
wolffd@0 465 obj.edgeArray = struct('from',[],'to',[],'arrow',[]);
wolffd@0 466 counter = 1;
wolffd@0 467 for i=1:obj.nnodes
wolffd@0 468 for j=1:obj.nnodes
wolffd@0 469 if(obj.adjMatrix(i,j))
wolffd@0 470 obj.edgeArray(counter) = struct('from',obj.nodeArray(i),'to',obj.nodeArray(j),'arrow',-1);
wolffd@0 471 obj.nodeArray(i).outedges = [obj.nodeArray(i).outedges,counter];
wolffd@0 472 obj.nodeArray(j).inedges = [obj.nodeArray(j).inedges,counter];
wolffd@0 473 counter = counter + 1;
wolffd@0 474 end
wolffd@0 475 end
wolffd@0 476 end
wolffd@0 477 obj.nedges = counter -1;
wolffd@0 478 end
wolffd@0 479
wolffd@0 480 function calculateMinMaxNodeSize(obj)
wolffd@0 481 % calculates the maximum and minimum node sizes in data units
wolffd@0 482 SCREEN_PROPORTION_MAX = 1/10;
wolffd@0 483 SCREEN_PROPORTION_MIN = 1/35;
wolffd@0 484 units = get(0,'Units');
wolffd@0 485 set(0,'Units','pixels');
wolffd@0 486 screensize = get(0,'ScreenSize');
wolffd@0 487 set(0,'Units',units);
wolffd@0 488 axunits = get(obj.ax,'Units');
wolffd@0 489 set(obj.ax,'Units','pixels');
wolffd@0 490 axsize = get(obj.ax,'Position');
wolffd@0 491 set(obj.ax,'Units',axunits);
wolffd@0 492 if(screensize(3) < screensize(4))
wolffd@0 493 dataUnitsPerPixel = abs(diff(xlim))/axsize(3);
wolffd@0 494 obj.minNodeSize = (SCREEN_PROPORTION_MIN*screensize(3))*dataUnitsPerPixel;
wolffd@0 495 obj.maxNodeSize = (SCREEN_PROPORTION_MAX*screensize(3))*dataUnitsPerPixel;
wolffd@0 496 else
wolffd@0 497 dataUnitsPerPixel = abs(diff(ylim))/axsize(4);
wolffd@0 498 obj.minNodeSize = (SCREEN_PROPORTION_MIN*screensize(4))*dataUnitsPerPixel;
wolffd@0 499 obj.maxNodeSize = (SCREEN_PROPORTION_MAX*screensize(4))*dataUnitsPerPixel;
wolffd@0 500 end
wolffd@0 501 end
wolffd@0 502
wolffd@0 503 function layoutNodes(obj)
wolffd@0 504 % Layout the nodes and edges according to the current layout
wolffd@0 505 % algorithm.
wolffd@0 506 if(obj.flipped)
wolffd@0 507 adj = obj.adjMatrix';
wolffd@0 508 else
wolffd@0 509 adj = obj.adjMatrix;
wolffd@0 510 end
wolffd@0 511 obj.currentLayout.dolayout(adj,obj.ax,obj.maxNodeSize);
wolffd@0 512 nodesize = obj.currentLayout.nodeSize();
wolffd@0 513 locs = obj.currentLayout.centers();
wolffd@0 514 for i=1:obj.nnodes
wolffd@0 515 node = obj.nodeArray(i);
wolffd@0 516 node.resize(nodesize);
wolffd@0 517 node.move(locs(i,1),locs(i,2));
wolffd@0 518 end
wolffd@0 519 end
wolffd@0 520
wolffd@0 521 function displayGraph(obj)
wolffd@0 522 % Display all of the nodes and edges.
wolffd@0 523 cla(obj.ax);
wolffd@0 524 obj.setFontSize();
wolffd@0 525 for i=1:obj.nnodes
wolffd@0 526 node = obj.nodeArray(i);
wolffd@0 527 node.fontSize = obj.fontSize;
wolffd@0 528 node.draw(obj.ax);
wolffd@0 529 end
wolffd@0 530 displayEdges(obj);
wolffd@0 531 end
wolffd@0 532
wolffd@0 533 function displayEdges(obj,indices)
wolffd@0 534 % Display or refresh the specified edges. If none specified, all
wolffd@0 535 % are refreshed. Currently only works with round nodes.
wolffd@0 536 figure(obj.fig);
wolffd@0 537 if(nargin < 2)
wolffd@0 538 indices = 1:obj.nedges;
wolffd@0 539 else
wolffd@0 540 indices = unique(indices);
wolffd@0 541 end
wolffd@0 542 for i=1:numel(indices)
wolffd@0 543 edge = obj.edgeArray(indices(i));
wolffd@0 544 [X,Y,Xarrow,Yarrow] = obj.calcPositions(edge);
wolffd@0 545 if(ishandle(edge.arrow))
wolffd@0 546 delete(edge.arrow)
wolffd@0 547 end
wolffd@0 548 hold on;
wolffd@0 549 edgeColor = obj.defaultEdgeColor;
wolffd@0 550 if ~isempty(obj.edgeColors)
wolffd@0 551 candidates = obj.edgeColors(findString(edge.from.label,obj.edgeColors(:,1)),:);
wolffd@0 552 if size(candidates,1)==1 && strcmpi(candidates(1,2),'all')
wolffd@0 553 edgeColor = candidates{1,3};
wolffd@0 554 else
wolffd@0 555 edgeCol = candidates(findString(edge.to.label,candidates(:,2)),3);
wolffd@0 556 if ~isempty(edgeCol); edgeColor = edgeCol{1}; end
wolffd@0 557 end
wolffd@0 558 end
wolffd@0 559 edge.arrow = plot(X,Y,'LineWidth',2,'HitTest','off','Color',edgeColor);
wolffd@0 560 if(~obj.undirected)
wolffd@0 561 arrowHead = obj.displayArrowHead(X,Y,Xarrow,Yarrow,edgeColor);
wolffd@0 562 edge.arrow = [edge.arrow arrowHead];
wolffd@0 563 end
wolffd@0 564 hold off;
wolffd@0 565 obj.edgeArray(indices(i)) = edge;
wolffd@0 566 end
wolffd@0 567 end
wolffd@0 568
wolffd@0 569 function arrowHead = displayArrowHead(obj,X,Y,Xarrow,Yarrow,arrowColor) %#ok
wolffd@0 570 % Displays the arrow head given the appropriate coordinates
wolffd@0 571 % calculated via the calcPositions() function.
wolffd@0 572
wolffd@0 573 arrowHead = patch('Faces' ,[1,2,3] ,...
wolffd@0 574 'Vertices' ,[Xarrow(1) Yarrow(1); Xarrow(2) Yarrow(2) ;X(2) Y(2)],...
wolffd@0 575 'FaceColor' ,arrowColor);
wolffd@0 576 end
wolffd@0 577
wolffd@0 578 function [X,Y,Xarrow,Yarrow] = calcPositions(obj,edge)
wolffd@0 579 % Helper function for displayEdges() - calculates edge and arrow
wolffd@0 580 % start and end positions in data units.
wolffd@0 581 X = [edge.from.xpos edge.to.xpos];
wolffd@0 582 Y = [edge.from.ypos edge.to.ypos];
wolffd@0 583 ratio = (Y(2) - Y(1))/(X(2)-X(1));
wolffd@0 584 if(isinf(ratio))
wolffd@0 585 ratio = realmax;
wolffd@0 586 end
wolffd@0 587 % dx: x-distance from node1 center to perimeter in direction of node2
wolffd@0 588 % dy: y-distance from node1 center to perimeter in direction of node2
wolffd@0 589 % ddx: x-distance from node1 perimeter to base of arrow head
wolffd@0 590 % ddy: y-distance from node1 perimeter to base of arrow head
wolffd@0 591 % dpx: x-offset away from edge in perpendicular direction, for arrow head
wolffd@0 592 % dpy: y-offset away from edge in perpendicular direction, for arrow head
wolffd@0 593
wolffd@0 594 arrowSize = obj.maxNodeSize/10;
wolffd@0 595 [dx,dy] = pol2cart(atan(ratio),edge.from.width/2);
wolffd@0 596 [ddx,ddy] = pol2cart(atan(ratio),arrowSize);
wolffd@0 597 ratio = 1/ratio; % now work out perpendicular directions.
wolffd@0 598 if(isinf(ratio))
wolffd@0 599 ratio = realmax;
wolffd@0 600 end
wolffd@0 601 [dpx dpy] = pol2cart(atan(ratio),arrowSize/2);
wolffd@0 602 ddx = abs(ddx); ddy = abs(ddy); dpx = abs(dpx); dpy = abs(dpy);
wolffd@0 603 dx = abs(dx); dy = abs(dy);
wolffd@0 604 if(X(1) < X(2))
wolffd@0 605 X(1) = X(1) + dx; X(2) = X(2) - dx;
wolffd@0 606 else
wolffd@0 607 X(1) = X(1) - dx; X(2) = X(2) + dx;
wolffd@0 608 end
wolffd@0 609 if(Y(1) < Y(2))
wolffd@0 610 Y(1) = Y(1) + dy; Y(2) = Y(2) - dy;
wolffd@0 611 else
wolffd@0 612 Y(1) = Y(1) - dy; Y(2) = Y(2) + dy;
wolffd@0 613 end
wolffd@0 614 if(X(1) <= X(2) && Y(1) <= Y(2))
wolffd@0 615 Xarrow(1) = X(2) - ddx - dpx; Xarrow(2) = X(2) - ddx + dpx;
wolffd@0 616 Yarrow(1) = Y(2) - ddy + dpy; Yarrow(2) = Y(2) - ddy - dpy;
wolffd@0 617 elseif(X(1) <= X(2) && Y(1) >= Y(2))
wolffd@0 618 Xarrow(1) = X(2) - ddx - dpx; Xarrow(2) = X(2) - ddx + dpx;
wolffd@0 619 Yarrow(1) = Y(2) + ddy - dpy; Yarrow(2) = Y(2) + ddy + dpy;
wolffd@0 620 elseif(X(1) >= X(2) && Y(1) <= Y(2))
wolffd@0 621 Xarrow(1) = X(2) + ddx - dpx; Xarrow(2) = X(2) + ddx + dpx;
wolffd@0 622 Yarrow(1) = Y(2) - ddy - dpy; Yarrow(2) = Y(2) - ddy + dpy;
wolffd@0 623 else % (X(1) >= (X(2) && Y(1) >= Y(2))
wolffd@0 624 Xarrow(1) = X(2) + ddx - dpx; Xarrow(2) = X(2) + ddx + dpx;
wolffd@0 625 Yarrow(1) = Y(2) + ddy + dpy; Yarrow(2) = Y(2) + ddy - dpy;
wolffd@0 626 end
wolffd@0 627 end
wolffd@0 628
wolffd@0 629 function addButtons(obj)
wolffd@0 630 % Add user interface buttons.
wolffd@0 631 if(~isempty(obj.toolbar))
wolffd@0 632 if(ishandle(obj.toolbar))
wolffd@0 633 delete(obj.toolbar);
wolffd@0 634 obj.toolbar = [];
wolffd@0 635 end
wolffd@0 636 end
wolffd@0 637 obj.toolbar = uitoolbar(obj.fig);
wolffd@0 638
wolffd@0 639 % button icons
wolffd@0 640 load glicons;
wolffd@0 641
wolffd@0 642 uipushtool(obj.toolbar,...
wolffd@0 643 'ClickedCallback' ,@obj.decreaseFontSize,...
wolffd@0 644 'TooltipString' ,'Decrease Font Size',...
wolffd@0 645 'CData' ,icons.downblue);
wolffd@0 646
wolffd@0 647 uipushtool(obj.toolbar,...
wolffd@0 648 'ClickedCallback' ,@obj.increaseFontSize,...
wolffd@0 649 'TooltipString' ,'Increase Font Size',...
wolffd@0 650 'CData' ,icons.upblue);
wolffd@0 651
wolffd@0 652 uipushtool(obj.toolbar,...
wolffd@0 653 'ClickedCallback' ,@obj.tightenAxes,...
wolffd@0 654 'TooltipString' ,'Tighten Axes',...
wolffd@0 655 'CData' ,icons.expand);
wolffd@0 656
wolffd@0 657 uipushtool(obj.toolbar,...
wolffd@0 658 'ClickedCallback' ,@obj.flip,...
wolffd@0 659 'TooltipString' ,'Flip/Reset Layout',...
wolffd@0 660 'CData' , icons.flip);
wolffd@0 661
wolffd@0 662 uipushtool(obj.toolbar,...
wolffd@0 663 'ClickedCallback' ,@obj.shrinkNodes,...
wolffd@0 664 'TooltipString' ,'Decrease Node Size',...
wolffd@0 665 'CData' , icons.downdarkblue);
wolffd@0 666
wolffd@0 667 uipushtool(obj.toolbar,...
wolffd@0 668 'ClickedCallback' ,@obj.growNodes,...
wolffd@0 669 'TooltipString' ,'Increase Node Size',...
wolffd@0 670 'CData' , icons.updarkblue);
wolffd@0 671
wolffd@0 672 if(~isempty(obj.layoutButtons))
wolffd@0 673 for i=1:numel(obj.layoutButtons)
wolffd@0 674 if(ishandle(obj.layoutButtons(i)))
wolffd@0 675 delete(obj.layoutButtons(i));
wolffd@0 676 end
wolffd@0 677 end
wolffd@0 678 obj.layoutButtons = [];
wolffd@0 679 end
wolffd@0 680
wolffd@0 681 layoutNames = fieldnames(obj.layouts);
wolffd@0 682 for i=1:numel(layoutNames)
wolffd@0 683 layout = obj.layouts.(layoutNames{i});
wolffd@0 684 layoutButton = uipushtool(obj.toolbar,...
wolffd@0 685 'ClickedCallback', @obj.layoutButtonPushed,...
wolffd@0 686 'TooltipString', layout.shortDescription,...
wolffd@0 687 'UserData' , layoutNames{i},...
wolffd@0 688 'Separator' , 'on',...
wolffd@0 689 'CData' , layout.image);
wolffd@0 690 obj.layoutButtons = [obj.layoutButtons,layoutButton];
wolffd@0 691 end
wolffd@0 692 end
wolffd@0 693
wolffd@0 694 function setFontSize(obj)
wolffd@0 695 % fontsize = obj.maxFontSize;
wolffd@0 696 fontSize = 20;
wolffd@0 697 maxchars = size(char(obj.nodeLabels),2);
wolffd@0 698 width = obj.nodeArray(1).width;
wolffd@0 699 height = obj.nodeArray(1).height;
wolffd@0 700 xpos = -10; ypos = -10;
wolffd@0 701 t = text(xpos,ypos,repmat('g',1,maxchars),...
wolffd@0 702 'FontUnits' , 'points' ,...
wolffd@0 703 'Units' , 'data' ,...
wolffd@0 704 'HorizontalAlignment' , 'center' ,...
wolffd@0 705 'VerticalAlignment' , 'middle' ,...
wolffd@0 706 'FontWeight' , 'demi' ,...
wolffd@0 707 'LineStyle' , 'none' ,...
wolffd@0 708 'Margin' , 0.01 ,...
wolffd@0 709 'FontSize' , fontSize ,...
wolffd@0 710 'Color' , 'w' );
wolffd@0 711
wolffd@0 712 extent = get(t,'Extent');
wolffd@0 713
wolffd@0 714 while(extent(3) > width || extent(4) > height)
wolffd@0 715 fontSize = fontSize - 1;
wolffd@0 716 if(fontSize < 2), break,end
wolffd@0 717 set(t,'FontSize',fontSize);
wolffd@0 718 extent = get(t,'Extent');
wolffd@0 719 end
wolffd@0 720 obj.fontSize = fontSize;
wolffd@0 721
wolffd@0 722 end
wolffd@0 723
wolffd@0 724 function asp = aspectRatio(obj)
wolffd@0 725 % Return the current aspect ratio of the figure, width/height
wolffd@0 726
wolffd@0 727 units = get(obj.ax,'Units');
wolffd@0 728 set(obj.ax,'Units','pixels');
wolffd@0 729 pos = get(obj.ax,'Position');
wolffd@0 730 set(obj.ax,'Units',units);
wolffd@0 731 asp = (pos(3)/pos(4));
wolffd@0 732
wolffd@0 733 end
wolffd@0 734
wolffd@0 735 function paperCrop(obj)
wolffd@0 736 % Make the papersize the same as the the figure size. This is
wolffd@0 737 % useful when saving as pdf.
wolffd@0 738 units = get(obj.fig,'Units');
wolffd@0 739 set(obj.fig,'Units','inches');
wolffd@0 740 pos = get(obj.fig,'Position');
wolffd@0 741 set(obj.fig,'Units',units);
wolffd@0 742 set(obj.fig,'PaperPositionMode','auto','PaperSize',pos(3:4));
wolffd@0 743 end
wolffd@0 744 %%
wolffd@0 745 % Callbacks
wolffd@0 746
wolffd@0 747 function layoutButtonPushed(obj,buttonPushed,varargin)
wolffd@0 748 % Called when a layout button is pushed.
wolffd@0 749 name = get(buttonPushed,'UserData');
wolffd@0 750 obj.currentLayout = obj.layouts.(name);
wolffd@0 751 axis square;
wolffd@0 752 obj.redraw;
wolffd@0 753 end
wolffd@0 754
wolffd@0 755 function windowResized(obj,varargin)
wolffd@0 756 % This function is called whenever the window is resized. It
wolffd@0 757 % redraws the whole graph.
wolffd@0 758 if(obj.isvisible)
wolffd@0 759 obj.redraw;
wolffd@0 760 obj.paperCrop();
wolffd@0 761 end
wolffd@0 762
wolffd@0 763 end
wolffd@0 764
wolffd@0 765 function mouseMoved(obj,varargin)
wolffd@0 766 % This function is called whenever the mouse moves within the
wolffd@0 767 % figure.
wolffd@0 768 if(obj.groupSelectionMode == 2)
wolffd@0 769 % Move all of the nodes & rectangle
wolffd@0 770 currentPoint = get(obj.ax,'CurrentPoint');
wolffd@0 771 xlimits = get(obj.ax,'XLim');
wolffd@0 772 ylimits = get(obj.ax,'YLim');
wolffd@0 773 sdims = obj.groupSelectedDims;
wolffd@0 774 xdiff = currentPoint(1,1) - obj.previousMouseLocation(1,1);
wolffd@0 775 ydiff = currentPoint(1,2) - obj.previousMouseLocation(1,2);
wolffd@0 776
wolffd@0 777 if(xdiff <=0)
wolffd@0 778 xdiff = max(xdiff,(xlimits(1)-sdims(1)));
wolffd@0 779 else
wolffd@0 780 xdiff = min(xdiff,xlimits(2)-sdims(2));
wolffd@0 781 end
wolffd@0 782 if(ydiff <=0)
wolffd@0 783 ydiff = max(ydiff,(ylimits(1)-sdims(3)));
wolffd@0 784 else
wolffd@0 785 ydiff = min(ydiff,ylimits(2)-sdims(4));
wolffd@0 786 end
wolffd@0 787 xnodepos = vertcat(obj.groupSelectedNodes.xpos) + xdiff;
wolffd@0 788 ynodepos = vertcat(obj.groupSelectedNodes.ypos) + ydiff;
wolffd@0 789 for i=1:numel(obj.groupSelectedNodes)
wolffd@0 790 obj.groupSelectedNodes(i).move(xnodepos(i),ynodepos(i));
wolffd@0 791 end
wolffd@0 792 recpos = get(obj.groupSelectedRect,'Position');
wolffd@0 793 recpos(1) = recpos(1) + xdiff;
wolffd@0 794 recpos(2) = recpos(2) + ydiff;
wolffd@0 795 obj.groupSelectedDims = [recpos(1),recpos(1)+recpos(3),recpos(2),recpos(2)+recpos(4)];
wolffd@0 796 set(obj.groupSelectedRect,'Position',recpos);
wolffd@0 797 edges = [obj.groupSelectedNodes.inedges,obj.groupSelectedNodes.outedges];
wolffd@0 798 obj.displayEdges(edges);
wolffd@0 799 obj.previousMouseLocation = currentPoint;
wolffd@0 800 else
wolffd@0 801 if(isempty(obj.selectedNode)), return,end
wolffd@0 802 currentPoint = get(obj.ax,'CurrentPoint');
wolffd@0 803 x = currentPoint(1,1); y = currentPoint(1,2);
wolffd@0 804 xl = xlim + [obj.selectedNode.width,-obj.selectedNode.width]/2;
wolffd@0 805 yl = ylim + [obj.selectedNode.height,-obj.selectedNode.height]/2;
wolffd@0 806 x = min(max(xl(1),x),xl(2));
wolffd@0 807 y = min(max(yl(1),y),yl(2));
wolffd@0 808 obj.selectedNode.move(x,y);
wolffd@0 809 obj.displayEdges([obj.selectedNode.inedges,obj.selectedNode.outedges]);
wolffd@0 810 end
wolffd@0 811 end
wolffd@0 812
wolffd@0 813 function buttonUp(obj,varargin)
wolffd@0 814 % This function executes when the mouse button is released.
wolffd@0 815 if(obj.groupSelectionMode == 2)
wolffd@0 816 obj.clearGroupSelection();
wolffd@0 817 return;
wolffd@0 818 end
wolffd@0 819 if(isempty(obj.selectedNode)),return,end
wolffd@0 820 obj.selectedNode.deselect();
wolffd@0 821
wolffd@0 822 obj.selectedNode.useFullLabel = false;
wolffd@0 823 obj.selectedNode.fontSize = obj.selectedFontSize;
wolffd@0 824 obj.selectedNode.redraw();
wolffd@0 825 obj.selectedNode = [];
wolffd@0 826 set(gcf,'Pointer','arrow');
wolffd@0 827 end
wolffd@0 828
wolffd@0 829 function axPressed(obj,varargin)
wolffd@0 830 % Called when the user selects the axes but not a node
wolffd@0 831 switch obj.groupSelectionMode
wolffd@0 832 case 0 % hasn't been selected yet
wolffd@0 833 xpos = vertcat(obj.nodeArray.xpos);
wolffd@0 834 ypos = vertcat(obj.nodeArray.ypos);
wolffd@0 835 p1 = get(obj.ax,'CurrentPoint');
wolffd@0 836 rbbox; % returns after box drawn
wolffd@0 837 p2 = get(obj.ax,'CurrentPoint');
wolffd@0 838 xleft = min(p1(1,1),p2(1,1));
wolffd@0 839 xright = max(p1(1,1),p2(1,1));
wolffd@0 840 ylower = min(p1(1,2),p2(1,2));
wolffd@0 841 yupper = max(p1(1,2),p2(1,2));
wolffd@0 842 selectedX = (xpos <= xright) & (xpos >= xleft);
wolffd@0 843 selectedY = (ypos <= yupper) & (ypos >= ylower);
wolffd@0 844 selected = selectedX & selectedY;
wolffd@0 845 if(~any(selected)),return,end
wolffd@0 846 obj.groupSelectionMode = 1;
wolffd@0 847 obj.groupSelectedNodes = obj.nodeArray(selected);
wolffd@0 848 for i=1:numel(obj.groupSelectedNodes)
wolffd@0 849 node = obj.groupSelectedNodes(i);
wolffd@0 850 node.select();
wolffd@0 851 node.redraw();
wolffd@0 852 end
wolffd@0 853
wolffd@0 854 w = obj.groupSelectedNodes(1).width/2;
wolffd@0 855 h = obj.groupSelectedNodes(1).height/2;
wolffd@0 856 x = vertcat(obj.groupSelectedNodes.xpos);
wolffd@0 857 y = vertcat(obj.groupSelectedNodes.ypos);
wolffd@0 858 minx = min(x)-w; maxx = max(x)+w; miny = min(y)-h; maxy = max(y)+h;
wolffd@0 859 obj.groupSelectedDims = [minx,maxx,miny,maxy];
wolffd@0 860 obj.groupSelectedRect = rectangle('Position',[minx,miny,maxx-minx,maxy-miny],'LineStyle','--','EdgeColor','r');
wolffd@0 861 case 1 % nodes selected
wolffd@0 862 obj.groupSelectionStage1();
wolffd@0 863 case 2 %not ever reached in this function
wolffd@0 864 obj.clearGroupSelection();
wolffd@0 865 end
wolffd@0 866 end
wolffd@0 867
wolffd@0 868 function groupSelectionStage1(obj)
wolffd@0 869 % Called after a group of nodes has been selected and the mouse
wolffd@0 870 % button has been pressed somewhere on the axes, (or on a node).
wolffd@0 871 p = get(obj.ax,'CurrentPoint');
wolffd@0 872 obj.previousMouseLocation = p;
wolffd@0 873 dims = obj.groupSelectedDims;
wolffd@0 874 if(p(1,1) >= dims(1) && p(1,1) <= dims(2) && p(1,2) >= dims(3) && p(1,2) <=dims(4))
wolffd@0 875 set(gcf,'Pointer','hand');
wolffd@0 876 obj.groupSelectionMode = 2;
wolffd@0 877 else
wolffd@0 878 obj.clearGroupSelection();
wolffd@0 879 end
wolffd@0 880 end
wolffd@0 881
wolffd@0 882 function clearGroupSelection(obj)
wolffd@0 883 % Clear a group selection
wolffd@0 884 if(ishandle(obj.groupSelectedRect))
wolffd@0 885 delete(obj.groupSelectedRect);
wolffd@0 886 end
wolffd@0 887 obj.groupSelectedRect = [];
wolffd@0 888 for i=1:numel(obj.groupSelectedNodes)
wolffd@0 889 obj.groupSelectedNodes(i).deselect();
wolffd@0 890 end
wolffd@0 891 obj.groupSelectedNodes = [];
wolffd@0 892 obj.groupSelectedDims = [];
wolffd@0 893 obj.groupSelectionMode = 0;
wolffd@0 894 set(gcf,'Pointer','arrow');
wolffd@0 895 end
wolffd@0 896
wolffd@0 897 function deleted(obj,varargin)
wolffd@0 898 % Called when the figure is deleted by the user.
wolffd@0 899 obj.isvisible = false;
wolffd@0 900 obj.clearGroupSelection();
wolffd@0 901 end
wolffd@0 902
wolffd@0 903 function singleClick(obj,node)
wolffd@0 904 % Called when a user single clicks on a node.
wolffd@0 905 obj.selectedNode = node;
wolffd@0 906 node.select();
wolffd@0 907 set(gcf,'Pointer','hand');
wolffd@0 908 obj.selectedFontSize = node.fontSize;
wolffd@0 909 node.useFullLabel = true;
wolffd@0 910 node.fontSize = max(15,node.fontSize*1.5);
wolffd@0 911 node.redraw();
wolffd@0 912 end
wolffd@0 913
wolffd@0 914 function doubleClick(obj,node)
wolffd@0 915 % Called when a user double clicks on a node
wolffd@0 916 if isempty(obj.doubleClickFn)
wolffd@0 917 description = node.description;
wolffd@0 918 if(~iscell(description))
wolffd@0 919 description = {description};
wolffd@0 920 end
wolffd@0 921 answer = inputdlg('',node.label,4,description);
wolffd@0 922 if(~isempty(answer))
wolffd@0 923 node.description = answer;
wolffd@0 924 end
wolffd@0 925 else
wolffd@0 926 obj.doubleClickFn(node.label);
wolffd@0 927 end
wolffd@0 928 end
wolffd@0 929
wolffd@0 930 function rightClick(obj,node) %#ok
wolffd@0 931 % Called when a user right clicks on a node
wolffd@0 932 if(node.isshaded)
wolffd@0 933 node.unshade();
wolffd@0 934 else
wolffd@0 935 node.shade();
wolffd@0 936 end
wolffd@0 937 end
wolffd@0 938
wolffd@0 939 function shiftClick(obj,node) %#ok
wolffd@0 940 % Called when a user shift clicks on a node
wolffd@0 941 display(node);
wolffd@0 942 end
wolffd@0 943
wolffd@0 944
wolffd@0 945 end
wolffd@0 946
wolffd@0 947 methods
wolffd@0 948 % Callbacks that can be called by the user programmatically.
wolffd@0 949 function shrinkNodes(obj,varargin)
wolffd@0 950 % Shrink the nodes to 95% of their original size, (but not smaller
wolffd@0 951 % than a calculated minimum.
wolffd@0 952 obj.clearGroupSelection();
wolffd@0 953 s = max(0.8*obj.nodeArray(1).width,obj.minNodeSize);
wolffd@0 954 obj.nodeArray(1).resize(s);
wolffd@0 955 obj.setFontSize();
wolffd@0 956 for i=1:obj.nnodes
wolffd@0 957 node = obj.nodeArray(i);
wolffd@0 958 node.fontSize = obj.fontSize;
wolffd@0 959 node.resize(s);
wolffd@0 960 end
wolffd@0 961 obj.displayEdges();
wolffd@0 962 end
wolffd@0 963
wolffd@0 964 function growNodes(obj,varargin)
wolffd@0 965 % Grow the nodes to 1/0.95 times their original size, (but not
wolffd@0 966 % larger than a calculated maximum.
wolffd@0 967 obj.clearGroupSelection();
wolffd@0 968 s = min(obj.nodeArray(1).width/0.8,1.5*obj.maxNodeSize);
wolffd@0 969 obj.nodeArray(1).resize(s);
wolffd@0 970 obj.setFontSize();
wolffd@0 971 for i=1:obj.nnodes
wolffd@0 972 node = obj.nodeArray(i);
wolffd@0 973 node.fontSize = obj.fontSize;
wolffd@0 974 node.resize(s);
wolffd@0 975 end
wolffd@0 976 obj.displayEdges();
wolffd@0 977 end
wolffd@0 978
wolffd@0 979 function increaseFontSize(obj,varargin)
wolffd@0 980 % Increase the fontsize of all the nodes by 0.5 points.
wolffd@0 981 current = get(obj.nodeArray(1).labelhandle,'FontSize');
wolffd@0 982 newsize = current + 1;
wolffd@0 983 for i=1:numel(obj.nodeArray)
wolffd@0 984 node = obj.nodeArray(i);
wolffd@0 985 node.fontSize = newsize;
wolffd@0 986 node.redraw();
wolffd@0 987 end
wolffd@0 988 end
wolffd@0 989
wolffd@0 990 function decreaseFontSize(obj,varargin)
wolffd@0 991 % Decrease the fontsize of all the nodes by 0.5 points.
wolffd@0 992 current = get(obj.nodeArray(1).labelhandle,'FontSize');
wolffd@0 993 newsize = max(current - 1,1);
wolffd@0 994 for i=1:numel(obj.nodeArray)
wolffd@0 995 node = obj.nodeArray(i);
wolffd@0 996 node.fontSize = newsize;
wolffd@0 997 node.redraw();
wolffd@0 998 end
wolffd@0 999 end
wolffd@0 1000
wolffd@0 1001 function XY = getNodePositions(obj)
wolffd@0 1002 % Return the current positions of the nodes. The bottom left
wolffd@0 1003 % corner is [0 0] and the top right is [1 1]. Node positions
wolffd@0 1004 % refer to the centre of a node.
wolffd@0 1005 XY = zeros(obj.nnodes, 2);
wolffd@0 1006 for i=1:obj.nnodes
wolffd@0 1007 XY(i, 1) = obj.nodeArray(i).xpos;
wolffd@0 1008 XY(i, 2) = obj.nodeArray(i).ypos;
wolffd@0 1009 end
wolffd@0 1010 end
wolffd@0 1011
wolffd@0 1012 function setNodePositions(obj, XY)
wolffd@0 1013 % Programmatically set the node positions
wolffd@0 1014 % XY(i, 1) is the xposition of node i, XY(i, 2) is the yposition.
wolffd@0 1015 for i=1:obj.nnodes
wolffd@0 1016 obj.nodeArray(i).move(XY(i, 1), XY(i, 2));
wolffd@0 1017 end
wolffd@0 1018 obj.displayGraph();
wolffd@0 1019 end
wolffd@0 1020
wolffd@0 1021 function moveNode(obj, nodeIndex, xpos, ypos)
wolffd@0 1022 % Programmatically set a node position.
wolffd@0 1023 obj.nodeArray(nodeIndex).move(xpos, ypos);
wolffd@0 1024 obj.displayGraph();
wolffd@0 1025 end
wolffd@0 1026
wolffd@0 1027 end
wolffd@0 1028 end