├── 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 |
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 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------