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

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