Skip to content

Commit 40120e5

Browse files
committed
Proper style leaves and key-based style rotation
1 parent ae956f6 commit 40120e5

File tree

4 files changed

+78
-9
lines changed

4 files changed

+78
-9
lines changed

iplotx/network.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def _add_vertices(self):
200200
vertex_labels[vid], **vertex_style.get("label", {})
201201
)
202202

203-
vertex_stylei = rotate_style(vertex_style, i)
203+
vertex_stylei = rotate_style(vertex_style, index=i, id=vid)
204204

205205
# Shape of the vertex (Patch)
206206
art = make_vertex_patch(**vertex_stylei)
@@ -278,7 +278,7 @@ def _add_directed_edges(self, labels=None):
278278
vpath1 = vertex_paths[vertex_indices[vid1]]
279279
vpath2 = vertex_paths[vertex_indices[vid2]]
280280

281-
edge_stylei = rotate_style(edge_style, i)
281+
edge_stylei = rotate_style(edge_style, index=i, id=(vid1, vid2))
282282

283283
# These are not the actual edges drawn, only stubs to establish
284284
# the styles which are then fed into the dynamic, optimised
@@ -343,7 +343,7 @@ def _add_undirected_edges(self, labels=None):
343343
vpath1 = vertex_paths[vertex_indices[vid1]]
344344
vpath2 = vertex_paths[vertex_indices[vid2]]
345345

346-
edge_stylei = rotate_style(edge_style, i)
346+
edge_stylei = rotate_style(edge_style, index=i, id=(vid1, vid2))
347347

348348
# These are not the actual edges drawn, only stubs to establish
349349
# the styles which are then fed into the dynamic, optimised

iplotx/plotting.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
)
1111
from .network import NetworkArtist
1212
from .groups import GroupingArtist
13+
from .styles import stylecontext
1314

1415

1516
def plot(
@@ -19,6 +20,7 @@ def plot(
1920
vertex_labels: Union[None, list, dict, pd.Series] = None,
2021
edge_labels: Union[None, Sequence] = None,
2122
ax: Union[None, object] = None,
23+
styles: Sequence[Union[str, dict]] = (),
2224
):
2325
"""Plot this network using the specified layout.
2426
@@ -28,11 +30,22 @@ def plot(
2830
vertex_labels (list, dict, or pandas.Series): The labels for the vertices. If None, no vertex labels
2931
will be drawn. If a list, the labels are taken from the list. If a dict, the keys
3032
should be the vertex IDs and the values should be the labels.
33+
edge_labels (Union[None, Sequence], optional): The labels for the edges. If None, no edge labels will be drawn. Defaults to None.
3134
ax (Union[None, object], optional): The axis to plot on. If None, a new figure and axis will be created. Defaults to None.
35+
**style: Additional keyword arguments are treated as style for the objects to plot.
3236
3337
Returns:
3438
A NetworkArtist object.
3539
"""
40+
if len(styles):
41+
with stylecontext(styles):
42+
return plot(
43+
network=network,
44+
layout=layout,
45+
grouping=grouping,
46+
edge_labels=edge_labels,
47+
)
48+
3649
if (network is None) and (grouping is None):
3750
raise ValueError("At least one of network or grouping must be provided.")
3851

iplotx/styles.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
from typing import Union, Sequence
1+
from typing import Union, Sequence, Hashable
22
from copy import deepcopy
33
from contextlib import contextmanager
44
import numpy as np
55
import pandas as pd
66

77

8+
style_leaves = (
9+
"edgecolor",
10+
"facecolor",
11+
"linewidth",
12+
"linestyle",
13+
"alpha",
14+
"zorder",
15+
)
16+
17+
818
default = {
919
"vertex": {
1020
"size": 20,
@@ -107,7 +117,8 @@ def _update(style: dict, current: dict):
107117
current[key] = value
108118
continue
109119

110-
if isinstance(value, dict):
120+
# Style leaves are by definition not to be recurred into
121+
if isinstance(value, dict) and (key not in style_leaves):
111122
_update(value, current[key])
112123
elif value is None:
113124
del current[key]
@@ -147,16 +158,29 @@ def stylecontext(style: Union[str, dict, Sequence]):
147158

148159
def rotate_style(
149160
style,
150-
i,
151-
props=("edgecolor", "facecolor", "linewidth", "linestyle", "alpha", "zorder"),
161+
index: Union[int, None] = None,
162+
id: Union[Hashable, None] = None,
163+
props=style_leaves,
152164
):
165+
if (index is None) and (id is None):
166+
raise ValueError(
167+
"At least one of 'index' or 'id' must be provided to rotate_style."
168+
)
169+
153170
style = deepcopy(style)
154171

155172
for prop in props:
156173
val = style.get(prop, None)
157174
if val is None:
158175
continue
159-
if isinstance(val, (tuple, list, np.ndarray, pd.Index, pd.Series)):
160-
style[prop] = val[i % len(val)]
176+
# NOTE: this assumes that these properties are leaves of the style tree
177+
# Btw: dict includes defaultdict, Couter, etc.
178+
if (id is not None) and isinstance(val, (dict, pd.Series)):
179+
# This works on both dict-like and Series
180+
style[prop] = val[id]
181+
elif (index is not None) and isinstance(
182+
val, (tuple, list, np.ndarray, pd.Index, pd.Series)
183+
):
184+
style[prop] = np.asarray(val)[index % len(val)]
161185

162186
return style

iplotx/tools/matplotlib.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,35 @@ def _compute_mid_coord(path):
101101
raise ValueError(
102102
"Curve type not straight and not squared/cubic Bezier, cannot compute mid point."
103103
)
104+
105+
106+
def _compute_group_path_with_vertex_padding(
107+
points,
108+
transform,
109+
vertexpadding=10,
110+
):
111+
"""Offset path for a group based on vertex padding.
112+
113+
At the input, the structure is [v1, v1, v1, v2, v2, v2, ...]
114+
"""
115+
116+
# Transform into figure coordinates
117+
trans = transform.transform
118+
trans_inv = transform.inverted().transform
119+
points = trans(points)
120+
121+
npoints = len(points) // 3
122+
vprev = points[-1]
123+
mprev = atan2(points[0, 1] - vprev[1], points[0, 0] - vprev[0])
124+
for i, vcur in enumerate(points[::3]):
125+
vnext = points[(i + 1) * 3]
126+
mnext = atan2(vnext[1] - vcur[1], vnext[0] - vcur[0])
127+
128+
mprev_orth = -1 / mprev
129+
points[i * 3] = vcur + vertexpadding * mprev_orth
130+
131+
vprev = vcur
132+
mprev = mnext
133+
134+
points = trans_inv(points)
135+
return points

0 commit comments

Comments
 (0)