├── README.md ├── circularGraph.PNG ├── circularGraph.m ├── circularGraph.mltbx ├── circularGraph.prj ├── demos.xml ├── example.m ├── html ├── example.html ├── example.png ├── example_01.png └── example_02.png ├── license.txt └── node.m /README.md: -------------------------------------------------------------------------------- 1 | # circularGraph 2 | 3 | ## Description 4 | A **circular graph** is a visualization of a network of nodes and their connections. The nodes are laid out along a circle, and the connections are drawn within the circle. Click on a node to make the connections that emanate from it more visible or less visible. Click on the **Show All** button to make all nodes and their connections visible. Click on the **Hide All** button to make all nodes and their connections less visible. 5 | 6 | ## Required Products 7 | * MATLAB 8.4 (R2014b) 8 | 9 | ## Tags 10 | adjacency, adjacency matrix, circle, circle graph, circle network, circular, circular graph, circular network, connection, connections, connectivity, directed graph, graph, graph theory, network, network plot, node, nodes, undirected graph 11 | -------------------------------------------------------------------------------- /circularGraph.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-kassebaum-mathworks/circularGraph/3a7926b6759e0f4c884295692ed710dfd2fd1525/circularGraph.PNG -------------------------------------------------------------------------------- /circularGraph.m: -------------------------------------------------------------------------------- 1 | classdef circularGraph < handle 2 | % CIRCULARGRAPH Plot an interactive circular graph to illustrate connections in a network. 3 | % 4 | %% Syntax 5 | % circularGraph(X) 6 | % circularGraph(X,'PropertyName',propertyvalue,...) 7 | % h = circularGraph(...) 8 | % 9 | %% Description 10 | % A 'circular graph' is a visualization of a network of nodes and their 11 | % connections. The nodes are laid out along a circle, and the connections 12 | % are drawn within the circle. Click on a node to make the connections that 13 | % emanate from it more visible or less visible. Click on the 'Show All' 14 | % button to make all nodes and their connections visible. Click on the 15 | % 'Hide All' button to make all nodes and their connections less visible. 16 | % 17 | % Required input arguments. 18 | % X : A symmetric matrix of numeric or logical values. 19 | % 20 | % Optional properties. 21 | % Colormap : A N by 3 matrix of [r g b] triples, where N is the 22 | % length(adjacenyMatrix). 23 | % Label : A cell array of N strings. 24 | %% 25 | % Copyright 2016 The MathWorks, Inc. 26 | properties 27 | Node = node(0,0); % Array of nodes 28 | ColorMap; % Colormap 29 | Label; % Cell array of strings 30 | ShowButton; % Turn all nodes on 31 | HideButton; % Turn all nodes off 32 | end 33 | 34 | methods 35 | function this = circularGraph(adjacencyMatrix,varargin) 36 | % Constructor 37 | p = inputParser; 38 | 39 | defaultColorMap = parula(length(adjacencyMatrix)); 40 | defaultLabel = cell(length(adjacencyMatrix)); 41 | for i = 1:length(defaultLabel) 42 | defaultLabel{i} = num2str(i); 43 | end 44 | 45 | addRequired(p,'adjacencyMatrix',@(x)(isnumeric(x) || islogical(x))); 46 | addParameter(p,'ColorMap',defaultColorMap,@(colormap)length(colormap) == length(adjacencyMatrix)); 47 | addParameter(p,'Label' ,defaultLabel ,@iscell); 48 | 49 | parse(p,adjacencyMatrix,varargin{:}); 50 | this.ColorMap = p.Results.ColorMap; 51 | this.Label = p.Results.Label; 52 | 53 | this.ShowButton = uicontrol(... 54 | 'Style','pushbutton',... 55 | 'Position',[0 40 80 40],... 56 | 'String','Show All',... 57 | 'Callback',@circularGraph.showNodes,... 58 | 'UserData',this); 59 | 60 | this.HideButton = uicontrol(... 61 | 'Style','pushbutton',... 62 | 'Position',[0 0 80 40],... 63 | 'String','Hide All',... 64 | 'Callback',@circularGraph.hideNodes,... 65 | 'UserData',this); 66 | 67 | fig = gcf; 68 | set(fig,... 69 | 'UserData',this,... 70 | 'CloseRequestFcn',@circularGraph.CloseRequestFcn); 71 | 72 | % Draw the nodes 73 | delete(this.Node); 74 | t = linspace(-pi,pi,length(adjacencyMatrix) + 1).'; % theta for each node 75 | extent = zeros(length(adjacencyMatrix),1); 76 | for i = 1:length(adjacencyMatrix) 77 | this.Node(i) = node(cos(t(i)),sin(t(i))); 78 | this.Node(i).Color = this.ColorMap(i,:); 79 | this.Node(i).Label = this.Label{i}; 80 | end 81 | 82 | % Find non-zero values of s and their indices 83 | [row,col,v] = find(adjacencyMatrix); 84 | 85 | % Calculate line widths based on values of s (stored in v). 86 | minLineWidth = 0.5; 87 | lineWidthCoef = 5; 88 | lineWidth = v./max(v); 89 | if sum(lineWidth) == numel(lineWidth) % all lines are the same width. 90 | lineWidth = repmat(minLineWidth,numel(lineWidth),1); 91 | else % lines of variable width. 92 | lineWidth = lineWidthCoef*lineWidth + minLineWidth; 93 | end 94 | 95 | % Draw connections on the Poincare hyperbolic disk. 96 | % 97 | % Equation of the circles on the disk: 98 | % x^2 + y^2 99 | % + 2*(u(2)-v(2))/(u(1)*v(2)-u(2)*v(1))*x 100 | % - 2*(u(1)-v(1))/(u(1)*v(2)-u(2)*v(1))*y + 1 = 0, 101 | % where u and v are points on the boundary. 102 | % 103 | % Standard form of equation of a circle 104 | % (x - x0)^2 + (y - y0)^2 = r^2 105 | % 106 | % Therefore we can identify 107 | % x0 = -(u(2)-v(2))/(u(1)*v(2)-u(2)*v(1)); 108 | % y0 = (u(1)-v(1))/(u(1)*v(2)-u(2)*v(1)); 109 | % r^2 = x0^2 + y0^2 - 1 110 | 111 | for i = 1:length(v) 112 | if row(i) ~= col(i) 113 | if abs(row(i) - col(i)) - length(adjacencyMatrix)/2 == 0 114 | % points are diametric, so draw a straight line 115 | u = [cos(t(row(i)));sin(t(row(i)))]; 116 | v = [cos(t(col(i)));sin(t(col(i)))]; 117 | this.Node(row(i)).Connection(end+1) = line(... 118 | [u(1);v(1)],... 119 | [u(2);v(2)],... 120 | 'LineWidth', lineWidth(i),... 121 | 'Color', this.ColorMap(row(i),:),... 122 | 'PickableParts','none'); 123 | else % points are not diametric, so draw an arc 124 | u = [cos(t(row(i)));sin(t(row(i)))]; 125 | v = [cos(t(col(i)));sin(t(col(i)))]; 126 | x0 = -(u(2)-v(2))/(u(1)*v(2)-u(2)*v(1)); 127 | y0 = (u(1)-v(1))/(u(1)*v(2)-u(2)*v(1)); 128 | r = sqrt(x0^2 + y0^2 - 1); 129 | thetaLim(1) = atan2(u(2)-y0,u(1)-x0); 130 | thetaLim(2) = atan2(v(2)-y0,v(1)-x0); 131 | 132 | if u(1) >= 0 && v(1) >= 0 133 | % ensure the arc is within the unit disk 134 | theta = [linspace(max(thetaLim),pi,50),... 135 | linspace(-pi,min(thetaLim),50)].'; 136 | else 137 | theta = linspace(thetaLim(1),thetaLim(2)).'; 138 | end 139 | 140 | this.Node(row(i)).Connection(end+1) = line(... 141 | r*cos(theta)+x0,... 142 | r*sin(theta)+y0,... 143 | 'LineWidth', lineWidth(i),... 144 | 'Color', this.ColorMap(row(i),:),... 145 | 'PickableParts','none'); 146 | end 147 | end 148 | end 149 | 150 | axis image; 151 | ax = gca; 152 | for i = 1:length(adjacencyMatrix) 153 | extent(i) = this.Node(i).Extent; 154 | end 155 | extent = max(extent(:)); 156 | ax.XLim = ax.XLim + extent*[-1 1]; 157 | fudgeFactor = 1.75; % Not sure why this is necessary. Eyeballed it. 158 | ax.YLim = ax.YLim + fudgeFactor*extent*[-1 1]; 159 | ax.Visible = 'off'; 160 | ax.SortMethod = 'depth'; 161 | 162 | fig = gcf; 163 | fig.Color = [1 1 1]; 164 | end 165 | 166 | end 167 | 168 | methods (Static = true) 169 | function showNodes(this,~) 170 | % Callback for 'Show All' button 171 | n = this.UserData.Node; 172 | for i = 1:length(n) 173 | n(i).Visible = true; 174 | end 175 | end 176 | 177 | function hideNodes(this,~) 178 | % Callback for 'Hide All' button 179 | n = this.UserData.Node; 180 | for i = 1:length(n) 181 | n(i).Visible = false; 182 | end 183 | end 184 | 185 | function CloseRequestFcn(this,~) 186 | % Callback for figure CloseRequestFcn 187 | c = this.UserData; 188 | for i = 1:length(c.Node) 189 | delete(c.Node(i)); 190 | end 191 | delete(gcf); 192 | end 193 | 194 | end 195 | 196 | end -------------------------------------------------------------------------------- /circularGraph.mltbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-kassebaum-mathworks/circularGraph/3a7926b6759e0f4c884295692ed710dfd2fd1525/circularGraph.mltbx -------------------------------------------------------------------------------- /circularGraph.prj: -------------------------------------------------------------------------------- 1 | 2 | 3 | circularGraph 4 | Paul Kassebaum 5 | paul.kassebaum@mathworks.com 6 | MathWorks 7 | Plot an interactive circular graph to illustrate connections in a network. 8 | A 'circular graph' is a visualization of a network of nodes and their connections. The nodes are laid out along a circle, and the connections are drawn within the circle. Click on a node to make the connections that emanate from it more visible or less visible. Click on the 'Show All' button to make all nodes and their connections visible. Click on the 'Hide All' button to make all nodes and their connections less visible. 9 | ${PROJECT_ROOT}\circularGraph.PNG 10 | 2.0 11 | ${PROJECT_ROOT}\circularGraph.mltbx 12 | 13 | 14 | 15 | 16 | 04027212-1e6d-49d4-84fa-f66cc29418de 17 | % List files contained in your toolbox folder that you would like to exclude 18 | % from packaging. Excludes should be listed relative to the toolbox folder. 19 | % Some examples of how to specify excludes are provided below: 20 | % 21 | % A single file in the toolbox folder: 22 | % .svn 23 | % 24 | % A single file in a subfolder of the toolbox folder: 25 | % example/.svn 26 | % 27 | % All files in a subfolder of the toolbox folder: 28 | % example/* 29 | % 30 | % All files of a certain name in all subfolders of the toolbox folder: 31 | % **/.svn 32 | % 33 | % All files matching a pattern in all subfolders of the toolbox folder: 34 | % **/*.bak 35 | % 36 | .git 37 | <?xml version="1.0" encoding="utf-8"?> 38 | <examples> 39 | <exampleCategory name="circularGraph"> 40 | <example name="example" type="html"> 41 | <file type="source">/html/example.html</file> 42 | <file type="main">/example.m</file> 43 | <file type="thumbnail">/html/example.png</file> 44 | <file type="image">/html/example_01.png</file> 45 | <file type="image">/html/example_02.png</file> 46 | </example> 47 | </exampleCategory> 48 | </examples> 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | C:\Users\pkasseba\Documents\MATLAB\circularGraph 68 | 69 | 70 | ${PROJECT_ROOT}\circularGraph.m 71 | ${PROJECT_ROOT}\circularGraph.PNG 72 | ${PROJECT_ROOT}\demos.xml 73 | ${PROJECT_ROOT}\example.m 74 | ${PROJECT_ROOT}\html 75 | ${PROJECT_ROOT}\license.txt 76 | ${PROJECT_ROOT}\node.m 77 | ${PROJECT_ROOT}\README.md 78 | 79 | 80 | 81 | 82 | 83 | C:\Users\pkasseba\Documents\MATLAB\circularGraph\circularGraph.mltbx 84 | 85 | 86 | 87 | C:\Program Files\MATLAB\R2016b 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | true 97 | 98 | 99 | 100 | 101 | true 102 | 103 | 104 | 105 | 106 | true 107 | 108 | 109 | 110 | 111 | true 112 | 113 | 114 | 115 | 116 | false 117 | false 118 | true 119 | false 120 | false 121 | false 122 | false 123 | false 124 | 6.2 125 | false 126 | true 127 | win64 128 | true 129 | 130 | 131 | -------------------------------------------------------------------------------- /demos.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | circularGraph 4 | toolbox 5 | HelpIcon.DEMOS 6 | 7 | A 'circular graph' is a visualization of a network of nodes and their connections. The nodes are laid out along a circle, and the connections are drawn within the circle. Click on a node to make the connections that emanate from it more visible or less visible. Click on the 'Show All' button to make all nodes and their connections visible. Click on the 'Hide All' button to make all nodes and their connections less visible. 8 | 9 | 10 | 11 | 12 | other 13 | example 14 | html/example.html 15 | 16 | 17 | -------------------------------------------------------------------------------- /example.m: -------------------------------------------------------------------------------- 1 | %% Circular Graph Examples 2 | % Copyright 2016 The MathWorks, Inc. 3 | 4 | %% 1. Adjacency matrix of 1s and 0s 5 | % Create an example adjacency matrix made up of ones and zeros. 6 | rng(0); 7 | x = rand(50); 8 | thresh = 0.93; 9 | x(x > thresh) = 1; 10 | x(x <= thresh) = 0; 11 | 12 | %% 13 | % Call CIRCULARGRAPH with only the adjacency matrix as an argument. 14 | circularGraph(x); 15 | 16 | %% 17 | % Click on a node to make the connections that emanate from it more visible 18 | % or less visible. Click on the 'Show All' button to make all nodes and 19 | % their connections visible. Click on the 'Hide All' button to make all 20 | % nodes and their connections less visible. 21 | 22 | %% 2. Supply custom properties 23 | % Create an example adjacency matrix made up of various values and supply 24 | % custom properties. 25 | rng(0); 26 | x = rand(20); 27 | thresh = 0.93; 28 | x(x > thresh) = 1; 29 | x(x <= thresh) = 0; 30 | for i = 1:numel(x) 31 | if x(i) > 0 32 | x(i) = rand(1,1); 33 | end 34 | end 35 | 36 | %% 37 | % Create custom node labels 38 | myLabel = cell(length(x)); 39 | for i = 1:length(x) 40 | myLabel{i} = num2str(round(1000000*rand(1,1))); 41 | end 42 | 43 | %% 44 | % Create custom colormap 45 | figure; 46 | myColorMap = lines(length(x)); 47 | 48 | circularGraph(x,'Colormap',myColorMap,'Label',myLabel); -------------------------------------------------------------------------------- /html/example.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | Circular Graph Examples

Circular Graph Examples

Contents

1. Adjacency matrix of 1s and 0s

Create an example adjacency matrix made up of ones and zeros.

rng(0);
 70 | x = rand(50);
 71 | thresh = 0.93;
 72 | x(x >  thresh) = 1;
 73 | x(x <= thresh) = 0;
 74 | 

Call CIRCULARGRAPH with only the adjacency matrix as an argument.

circularGraph(x);
 75 | 

Click on a node to make the connections that emanate from it more visible or less visible. Click on the 'Show All' button to make all nodes and their connections visible. Click on the 'Hide All' button to make all nodes and their connections less visible.

2. Supply custom properties

Create an example adjacency matrix made up of various values and supply custom properties.

rng(0);
 76 | x = rand(20);
 77 | thresh = 0.93;
 78 | x(x >  thresh) = 1;
 79 | x(x <= thresh) = 0;
 80 | for i = 1:numel(x)
 81 |   if x(i) > 0
 82 |     x(i) = rand(1,1);
 83 |   end
 84 | end
 85 | 

Create custom node labels

myLabel = cell(length(x));
 86 | for i = 1:length(x)
 87 |   myLabel{i} = num2str(round(1000000*rand(1,1)));
 88 | end
 89 | 

Create custom colormap

figure;
 90 | myColorMap = lines(length(x));
 91 | 
 92 | circularGraph(x,'Colormap',myColorMap,'Label',myLabel);
 93 | 
-------------------------------------------------------------------------------- /html/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-kassebaum-mathworks/circularGraph/3a7926b6759e0f4c884295692ed710dfd2fd1525/html/example.png -------------------------------------------------------------------------------- /html/example_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-kassebaum-mathworks/circularGraph/3a7926b6759e0f4c884295692ed710dfd2fd1525/html/example_01.png -------------------------------------------------------------------------------- /html/example_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paul-kassebaum-mathworks/circularGraph/3a7926b6759e0f4c884295692ed710dfd2fd1525/html/example_02.png -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, The MathWorks, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | * Neither the name of the The MathWorks, Inc. nor the names 14 | of its contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /node.m: -------------------------------------------------------------------------------- 1 | classdef node < handle 2 | % NODE Helper class for circularGraph. Not intended for direct user manipulation. 3 | %% 4 | % Copyright 2016 The MathWorks, Inc. 5 | properties (Access = public) 6 | Label = ''; % String 7 | Connection = line(0,0); % Array of lines 8 | Position; % [x,y] coordinates 9 | Color = [0 0 0]; % [r g b] 10 | Visible = true; % Logical true or false 11 | end 12 | 13 | properties (Access = public, Dependent = true) 14 | Extent; % Width of text label 15 | end 16 | 17 | properties (Access = private) 18 | TextLabel; % Text graphics object 19 | NodeMarker; % Line that makes the node visible 20 | Marker = 'o'; % Marker symbol when the node is 'on' 21 | end 22 | 23 | properties (Access = private, Constant) 24 | labelOffsetFactor = 1.1; 25 | end 26 | 27 | methods 28 | function this = node(x,y) 29 | % Constructor 30 | this.Position = [x,y]; 31 | this.Connection = line(0,0); 32 | makeLine(this); 33 | end 34 | 35 | function makeLine(this) 36 | % Make the node's line graphic object 37 | this.NodeMarker = line(... 38 | this.Position(1),... 39 | this.Position(2),... 40 | 2,... 41 | 'Color',this.Color,... 42 | 'Marker',this.Marker,... 43 | 'LineStyle','none',... 44 | 'PickableParts','all',... 45 | 'ButtonDownFcn',@node.ButtonDownFcn,... 46 | 'UserData',this); 47 | end 48 | 49 | function set.Visible(this,value) 50 | this.Visible = value; 51 | updateVisible(this); 52 | end 53 | 54 | function set.Color(this,value) 55 | this.Color = value; 56 | updateColor(this); 57 | end 58 | 59 | function set.Label(this,value) 60 | this.Label = value; 61 | updateTextLabel(this); 62 | end 63 | 64 | function value = get.Extent(this) 65 | value = this.TextLabel.Extent(3); 66 | end 67 | 68 | function updateVisible(this) 69 | if this.Visible 70 | this.NodeMarker.Marker = 'o'; 71 | set(this.Connection,'Color',this.Color); 72 | for i = 1:length(this.Connection) 73 | this.Connection(i).ZData = ones(size(this.Connection(i).XData)); 74 | end 75 | else 76 | this.NodeMarker.Marker = 'x'; 77 | set(this.Connection,'Color',0.9*[1 1 1]); 78 | for i = 1:length(this.Connection) 79 | this.Connection(i).ZData = zeros(size(this.Connection(i).XData)); 80 | end 81 | end 82 | end 83 | 84 | function updateColor(this) 85 | this.NodeMarker.Color = this.Color; 86 | set(this.Connection,'Color',this.Color); 87 | end 88 | 89 | function updateTextLabel(this) 90 | delete(this.TextLabel); 91 | 92 | x = this.Position(1); 93 | y = this.Position(2); 94 | t = atan2(y,x); 95 | 96 | this.TextLabel = text(0,0,this.Label); 97 | 98 | this.TextLabel.Position = node.labelOffsetFactor*this.Position; 99 | if abs(t) > pi/2 100 | this.TextLabel.Rotation = 180*(t/pi + 1); 101 | this.TextLabel.HorizontalAlignment = 'right'; 102 | else 103 | this.TextLabel.Rotation = t*180/pi; 104 | end 105 | end 106 | 107 | function delete(this) 108 | % Destructor 109 | delete(this.Connection(:)) 110 | delete(this.TextLabel); 111 | delete(this.NodeMarker); 112 | delete(this); 113 | end 114 | 115 | end 116 | 117 | methods (Static = true) 118 | function ButtonDownFcn(this,~) 119 | n = this.UserData; 120 | if n.Visible 121 | n.Visible = false; 122 | else 123 | n.Visible = true; 124 | end 125 | end 126 | end 127 | end --------------------------------------------------------------------------------