Skip to content

Commit 4fcfc33

Browse files
committed
Add Graph VertexLabeling property
1 parent 72094ba commit 4fcfc33

File tree

1 file changed

+85
-1
lines changed

1 file changed

+85
-1
lines changed

mathicsscript/format.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Format Mathics objects
33
"""
44

5+
import random
56
import networkx as nx
67

78

@@ -57,6 +58,87 @@ def format_output(obj, expr, format=None):
5758
return boxes
5859

5960

61+
def hierarchy_pos(G, root=None, width=1.0, vert_gap=0.2, vert_loc=0, xcenter=0.5):
62+
63+
"""
64+
From Joel's answer at https://stackoverflow.com/a/29597209/2966723.
65+
Licensed under Creative Commons Attribution-Share Alike
66+
67+
If the graph is a tree this will return the positions to plot this in a
68+
hierarchical layout.
69+
70+
G: the graph (must be a tree)
71+
72+
root: the root node of current branch
73+
- if the tree is directed and this is not given,
74+
the root will be found and used
75+
- if the tree is directed and this is given, then
76+
the positions will be just for the descendants of this node.
77+
- if the tree is undirected and not given,
78+
then a random choice will be used.
79+
80+
width: horizontal space allocated for this branch - avoids overlap with other branches
81+
82+
vert_gap: gap between levels of hierarchy
83+
84+
vert_loc: vertical location of root
85+
86+
xcenter: horizontal location of root
87+
"""
88+
if not nx.is_tree(G):
89+
raise TypeError("cannot use hierarchy_pos on a graph that is not a tree")
90+
91+
if root is None:
92+
if isinstance(G, nx.DiGraph):
93+
root = next(
94+
iter(nx.topological_sort(G))
95+
) # allows back compatibility with nx version 1.11
96+
else:
97+
root = random.choice(list(G.nodes))
98+
99+
def _hierarchy_pos(
100+
G, root, width=1.0, vert_gap=0.2, vert_loc=0, xcenter=0.5, pos=None, parent=None
101+
):
102+
"""
103+
see hierarchy_pos docstring for most arguments
104+
105+
pos: a dict saying where all nodes go if they have been assigned
106+
parent: parent of this branch. - only affects it if non-directed
107+
108+
"""
109+
110+
if pos is None:
111+
pos = {root: (xcenter, vert_loc)}
112+
else:
113+
pos[root] = (xcenter, vert_loc)
114+
children = list(G.neighbors(root))
115+
if not isinstance(G, nx.DiGraph) and parent is not None:
116+
children.remove(parent)
117+
if len(children) != 0:
118+
dx = width / len(children)
119+
nextx = xcenter - width / 2 - dx / 2
120+
for child in children:
121+
nextx += dx
122+
pos = _hierarchy_pos(
123+
G,
124+
child,
125+
width=dx,
126+
vert_gap=vert_gap,
127+
vert_loc=vert_loc - vert_gap,
128+
xcenter=nextx,
129+
pos=pos,
130+
parent=root,
131+
)
132+
return pos
133+
134+
return _hierarchy_pos(G, root, width, vert_gap, vert_loc, xcenter)
135+
136+
137+
def tree_layout(G):
138+
root = G.root if hasattr(G, "root") else None
139+
return hierarchy_pos(G, root=root)
140+
141+
60142
NETWORKX_LAYOUTS = {
61143
"circular": nx.circular_layout,
62144
"multipartite": nx.multipartite_layout,
@@ -65,7 +147,7 @@ def format_output(obj, expr, format=None):
65147
"shell": nx.shell_layout,
66148
"spectral": nx.spectral_layout,
67149
"spring": nx.spring_layout,
68-
"tree": nx.planar_layout,
150+
"tree": tree_layout,
69151
}
70152

71153

@@ -82,6 +164,8 @@ def format_graph(G, options):
82164
if not isinstance(plot_theme, str):
83165
plot_theme = plot_theme.get_string_value()
84166
layout_fn = NETWORKX_LAYOUTS.get(plot_theme, None)
167+
else:
168+
layout_fn = None
85169

86170
if layout_fn:
87171
nx.draw(G, pos=layout_fn(G), with_labels=vertex_labeling)

0 commit comments

Comments
 (0)