iplotx 0.6.8__py3-none-any.whl → 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iplotx/edge/__init__.py +5 -3
- iplotx/edge/arrow.py +30 -5
- iplotx/edge/geometry.py +15 -15
- iplotx/network.py +8 -0
- iplotx/style/__init__.py +13 -61
- iplotx/style/leaf_info.py +3 -1
- iplotx/style/library.py +1 -1
- iplotx/tree.py +14 -0
- iplotx/utils/style.py +111 -0
- iplotx/version.py +1 -1
- iplotx/vertex.py +5 -1
- {iplotx-0.6.8.dist-info → iplotx-0.8.0.dist-info}/METADATA +1 -1
- {iplotx-0.6.8.dist-info → iplotx-0.8.0.dist-info}/RECORD +14 -14
- {iplotx-0.6.8.dist-info → iplotx-0.8.0.dist-info}/WHEEL +0 -0
iplotx/edge/__init__.py
CHANGED
|
@@ -352,9 +352,9 @@ class EdgeCollection(mpl.collections.PatchCollection):
|
|
|
352
352
|
tension = 0
|
|
353
353
|
ports = None
|
|
354
354
|
|
|
355
|
-
# Scale
|
|
355
|
+
# Scale shrink by dpi
|
|
356
356
|
dpi = self.figure.dpi if hasattr(self, "figure") else 72.0
|
|
357
|
-
|
|
357
|
+
shrink = dpi / 72.0 * edge_stylei.pop("shrink", 0)
|
|
358
358
|
|
|
359
359
|
# False is a synonym for "none"
|
|
360
360
|
waypoints = edge_stylei.get("waypoints", "none")
|
|
@@ -380,7 +380,7 @@ class EdgeCollection(mpl.collections.PatchCollection):
|
|
|
380
380
|
waypoints=waypoints,
|
|
381
381
|
ports=ports,
|
|
382
382
|
layout_coordinate_system=self._vertex_collection.get_layout_coordinate_system(),
|
|
383
|
-
|
|
383
|
+
shrink=shrink,
|
|
384
384
|
)
|
|
385
385
|
|
|
386
386
|
offset = edge_stylei.get("offset", 0)
|
|
@@ -720,6 +720,8 @@ def make_stub_patch(**kwargs):
|
|
|
720
720
|
"cmap",
|
|
721
721
|
"norm",
|
|
722
722
|
"split",
|
|
723
|
+
"shrink",
|
|
724
|
+
# DEPRECATED
|
|
723
725
|
"padding",
|
|
724
726
|
]
|
|
725
727
|
for prop in forbidden_props:
|
iplotx/edge/arrow.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Module for edge arrows in iplotx.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Never
|
|
5
|
+
from typing import Never, Optional
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import matplotlib as mpl
|
|
@@ -149,10 +149,35 @@ class EdgeArrowCollection(mpl.collections.PatchCollection):
|
|
|
149
149
|
super().draw(renderer)
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
def make_arrow_patch(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
def make_arrow_patch(
|
|
153
|
+
marker: str = "|>",
|
|
154
|
+
width: float = 8,
|
|
155
|
+
height: Optional[float | str] = None,
|
|
156
|
+
**kwargs,
|
|
157
|
+
):
|
|
158
|
+
"""Make a patch of the given marker shape and size.
|
|
159
|
+
|
|
160
|
+
Parameters:
|
|
161
|
+
marker: The marker shape to use. Supported markers are:
|
|
162
|
+
"|>", "|/", "|\\", ">", "<", ">>", ")>", ")", "(", "]", "[", "|",
|
|
163
|
+
"x", "s", "d", "p", "q". Dashes at the start of this string will
|
|
164
|
+
be ignored, so "->" is equivalent to ">".
|
|
165
|
+
width: The width of the marker in points. Height is by default 1.3 the
|
|
166
|
+
width, unless specified separately.
|
|
167
|
+
height: The height of the marker in points. If not specified, it is
|
|
168
|
+
1.3 times the width. This can also be the string "width", in which
|
|
169
|
+
case the height will be equal to the width.
|
|
170
|
+
**kwargs: Additional keyword arguments passed to the PathPatch.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
A pair with the patch and the max size of the patch in points.
|
|
174
|
+
"""
|
|
175
|
+
# Forget any leading dashes
|
|
176
|
+
marker = marker.lstrip("-")
|
|
177
|
+
|
|
178
|
+
if height is None:
|
|
179
|
+
height = width * 1.3
|
|
180
|
+
elif height == "width":
|
|
156
181
|
height = width
|
|
157
182
|
|
|
158
183
|
# Normalise by the max size, this is taken care of in _transforms
|
iplotx/edge/geometry.py
CHANGED
|
@@ -64,7 +64,7 @@ def _compute_loops_per_angle(nloops, angles):
|
|
|
64
64
|
]
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def _get_shorter_edge_coords(vpath, vsize, theta,
|
|
67
|
+
def _get_shorter_edge_coords(vpath, vsize, theta, shrink=0):
|
|
68
68
|
# Bound theta from -pi to pi (why is that not guaranteed?)
|
|
69
69
|
theta = (theta + pi) % (2 * pi) - pi
|
|
70
70
|
|
|
@@ -105,8 +105,8 @@ def _get_shorter_edge_coords(vpath, vsize, theta, padding=0):
|
|
|
105
105
|
|
|
106
106
|
ve = ve * vsize
|
|
107
107
|
|
|
108
|
-
#
|
|
109
|
-
ve +=
|
|
108
|
+
# Shrink (assuming dpi scaling is already applied to the shrink)
|
|
109
|
+
ve += shrink * np.array([np.cos(theta), np.sin(theta)])
|
|
110
110
|
|
|
111
111
|
return ve
|
|
112
112
|
|
|
@@ -145,12 +145,12 @@ def _compute_loop_path(
|
|
|
145
145
|
angle2,
|
|
146
146
|
trans_inv,
|
|
147
147
|
looptension,
|
|
148
|
-
|
|
148
|
+
shrink=0,
|
|
149
149
|
):
|
|
150
150
|
# Shorten at starting angle
|
|
151
|
-
start = _get_shorter_edge_coords(vpath, vsize, angle1,
|
|
151
|
+
start = _get_shorter_edge_coords(vpath, vsize, angle1, shrink) + vcoord_fig
|
|
152
152
|
# Shorten at end angle
|
|
153
|
-
end = _get_shorter_edge_coords(vpath, vsize, angle2,
|
|
153
|
+
end = _get_shorter_edge_coords(vpath, vsize, angle2, shrink) + vcoord_fig
|
|
154
154
|
|
|
155
155
|
aux1 = (start - vcoord_fig) * looptension + vcoord_fig
|
|
156
156
|
aux2 = (end - vcoord_fig) * looptension + vcoord_fig
|
|
@@ -182,7 +182,7 @@ def _compute_edge_path_straight(
|
|
|
182
182
|
trans,
|
|
183
183
|
trans_inv,
|
|
184
184
|
layout_coordinate_system: str = "cartesian",
|
|
185
|
-
|
|
185
|
+
shrink: float = 0,
|
|
186
186
|
**kwargs,
|
|
187
187
|
):
|
|
188
188
|
if layout_coordinate_system not in ("cartesian", "polar"):
|
|
@@ -211,11 +211,11 @@ def _compute_edge_path_straight(
|
|
|
211
211
|
theta = atan2(*((vcoord_fig[1] - vcoord_fig[0])[::-1]))
|
|
212
212
|
|
|
213
213
|
# Shorten at starting vertex
|
|
214
|
-
vs = _get_shorter_edge_coords(vpath_fig[0], vsize_fig[0], theta,
|
|
214
|
+
vs = _get_shorter_edge_coords(vpath_fig[0], vsize_fig[0], theta, shrink) + vcoord_fig[0]
|
|
215
215
|
points.append(vs)
|
|
216
216
|
|
|
217
217
|
# Shorten at end vertex
|
|
218
|
-
ve = _get_shorter_edge_coords(vpath_fig[1], vsize_fig[1], theta + pi,
|
|
218
|
+
ve = _get_shorter_edge_coords(vpath_fig[1], vsize_fig[1], theta + pi, shrink) + vcoord_fig[1]
|
|
219
219
|
points.append(ve)
|
|
220
220
|
|
|
221
221
|
codes = ["MOVETO", "LINETO"]
|
|
@@ -237,7 +237,7 @@ def _compute_edge_path_waypoints(
|
|
|
237
237
|
layout_coordinate_system: str = "cartesian",
|
|
238
238
|
points_per_curve: int = 30,
|
|
239
239
|
ports: Pair[Optional[str]] = (None, None),
|
|
240
|
-
|
|
240
|
+
shrink: float = 0,
|
|
241
241
|
**kwargs,
|
|
242
242
|
):
|
|
243
243
|
if not isinstance(waypoints, str):
|
|
@@ -263,7 +263,7 @@ def _compute_edge_path_waypoints(
|
|
|
263
263
|
|
|
264
264
|
# Shorten at vertex border
|
|
265
265
|
vshorts[i] = (
|
|
266
|
-
_get_shorter_edge_coords(vpath_fig[i], vsize_fig[i], thetas[i],
|
|
266
|
+
_get_shorter_edge_coords(vpath_fig[i], vsize_fig[i], thetas[i], shrink)
|
|
267
267
|
+ vcoord_fig[i]
|
|
268
268
|
)
|
|
269
269
|
|
|
@@ -293,7 +293,7 @@ def _compute_edge_path_waypoints(
|
|
|
293
293
|
|
|
294
294
|
# Shorten at vertex border
|
|
295
295
|
vshorts[i] = (
|
|
296
|
-
_get_shorter_edge_coords(vpath_fig[i], vsize_fig[i], thetas[i],
|
|
296
|
+
_get_shorter_edge_coords(vpath_fig[i], vsize_fig[i], thetas[i], shrink)
|
|
297
297
|
+ vcoord_fig[i]
|
|
298
298
|
)
|
|
299
299
|
|
|
@@ -343,7 +343,7 @@ def _compute_edge_path_waypoints(
|
|
|
343
343
|
|
|
344
344
|
# Shorten at vertex border
|
|
345
345
|
vshort = (
|
|
346
|
-
_get_shorter_edge_coords(vpath_fig[i], vsize_fig[i], theta,
|
|
346
|
+
_get_shorter_edge_coords(vpath_fig[i], vsize_fig[i], theta, shrink) + vcoord_fig[i]
|
|
347
347
|
)
|
|
348
348
|
thetas.append(theta)
|
|
349
349
|
vshorts.append(vshort)
|
|
@@ -391,7 +391,7 @@ def _compute_edge_path_curved(
|
|
|
391
391
|
trans,
|
|
392
392
|
trans_inv,
|
|
393
393
|
ports: Pair[Optional[str]] = (None, None),
|
|
394
|
-
|
|
394
|
+
shrink: float = 0,
|
|
395
395
|
):
|
|
396
396
|
"""Shorten the edge path along a cubic Bezier between the vertex centres.
|
|
397
397
|
|
|
@@ -445,7 +445,7 @@ def _compute_edge_path_curved(
|
|
|
445
445
|
for i in range(2):
|
|
446
446
|
thetas[i] = atan2(*((auxs[i] - vcoord_fig[i])[::-1]))
|
|
447
447
|
vs[i] = (
|
|
448
|
-
_get_shorter_edge_coords(vpath_fig[i], vsize_fig[i], thetas[i],
|
|
448
|
+
_get_shorter_edge_coords(vpath_fig[i], vsize_fig[i], thetas[i], shrink) + vcoord_fig[i]
|
|
449
449
|
)
|
|
450
450
|
|
|
451
451
|
path = {
|
iplotx/network.py
CHANGED
|
@@ -162,6 +162,8 @@ class NetworkArtist(mpl.artist.Artist):
|
|
|
162
162
|
"""Get VertexCollection artist."""
|
|
163
163
|
return self._vertices
|
|
164
164
|
|
|
165
|
+
get_nodes = get_vertices
|
|
166
|
+
|
|
165
167
|
def get_edges(self):
|
|
166
168
|
"""Get EdgeCollection artist."""
|
|
167
169
|
return self._edges
|
|
@@ -170,6 +172,8 @@ class NetworkArtist(mpl.artist.Artist):
|
|
|
170
172
|
"""Get list of vertex label artists."""
|
|
171
173
|
return self._vertices.get_labels()
|
|
172
174
|
|
|
175
|
+
get_node_labels = get_vertex_labels
|
|
176
|
+
|
|
173
177
|
def get_edge_labels(self):
|
|
174
178
|
"""Get list of edge label artists."""
|
|
175
179
|
return self._edges.get_labels()
|
|
@@ -211,6 +215,10 @@ class NetworkArtist(mpl.artist.Artist):
|
|
|
211
215
|
return vertex_layout_df
|
|
212
216
|
|
|
213
217
|
def _get_label_series(self, kind):
|
|
218
|
+
# Equivalence vertex/node
|
|
219
|
+
if kind == "node":
|
|
220
|
+
kind = "vertex"
|
|
221
|
+
|
|
214
222
|
if "label" in self._ipx_internal_data[f"{kind}_df"].columns:
|
|
215
223
|
return self._ipx_internal_data[f"{kind}_df"]["label"]
|
|
216
224
|
else:
|
iplotx/style/__init__.py
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main style module for iplotx.
|
|
3
|
+
"""
|
|
4
|
+
|
|
1
5
|
from typing import (
|
|
2
6
|
Any,
|
|
3
7
|
Iterable,
|
|
4
8
|
Optional,
|
|
5
9
|
Sequence,
|
|
6
10
|
)
|
|
7
|
-
from collections import defaultdict
|
|
8
11
|
from collections.abc import Hashable
|
|
9
12
|
from contextlib import contextmanager
|
|
10
13
|
import numpy as np
|
|
11
14
|
import pandas as pd
|
|
12
15
|
|
|
13
|
-
from ..utils.style import
|
|
16
|
+
from ..utils.style import (
|
|
17
|
+
copy_with_deep_values,
|
|
18
|
+
sanitize_leaves,
|
|
19
|
+
update_style,
|
|
20
|
+
sanitize_ambiguous,
|
|
21
|
+
)
|
|
14
22
|
from .library import style_library
|
|
15
23
|
from .leaf_info import (
|
|
16
24
|
style_leaves,
|
|
@@ -83,72 +91,16 @@ def merge_styles(
|
|
|
83
91
|
Returns:
|
|
84
92
|
The composite style as a dict.
|
|
85
93
|
"""
|
|
86
|
-
try:
|
|
87
|
-
import networkx as nx
|
|
88
|
-
except ImportError:
|
|
89
|
-
nx = None
|
|
90
|
-
|
|
91
|
-
def _sanitize_leaves(style: dict):
|
|
92
|
-
for key, value in style.items():
|
|
93
|
-
if key in style_leaves:
|
|
94
|
-
# Networkx has a few lazy data structures
|
|
95
|
-
# TODO: move this code to provider
|
|
96
|
-
if nx is not None:
|
|
97
|
-
if isinstance(value, nx.classes.reportviews.NodeView):
|
|
98
|
-
style[key] = dict(value)
|
|
99
|
-
elif isinstance(value, nx.classes.reportviews.EdgeViewABC):
|
|
100
|
-
style[key] = [v for *e, v in value]
|
|
101
|
-
|
|
102
|
-
elif isinstance(value, dict):
|
|
103
|
-
_sanitize_leaves(value)
|
|
104
|
-
|
|
105
|
-
def _update(style: dict, current: dict):
|
|
106
|
-
for key, value in style.items():
|
|
107
|
-
if key not in current:
|
|
108
|
-
current[key] = value
|
|
109
|
-
continue
|
|
110
|
-
|
|
111
|
-
# Style non-leaves are either recurred into or deleted
|
|
112
|
-
if key not in style_leaves:
|
|
113
|
-
if isinstance(value, dict):
|
|
114
|
-
_update(value, current[key])
|
|
115
|
-
elif value is None:
|
|
116
|
-
del current[key]
|
|
117
|
-
else:
|
|
118
|
-
raise ValueError(
|
|
119
|
-
f"Setting non-leaf style value to a non-dict: {key}, {value}",
|
|
120
|
-
)
|
|
121
|
-
else:
|
|
122
|
-
# Style leaves could be incomplete, ensure a sensible default
|
|
123
|
-
if value is None:
|
|
124
|
-
del current[key]
|
|
125
|
-
continue
|
|
126
|
-
|
|
127
|
-
if not isinstance(value, dict):
|
|
128
|
-
current[key] = value
|
|
129
|
-
continue
|
|
130
|
-
|
|
131
|
-
if hasattr(value, "default_factory"):
|
|
132
|
-
current[key] = value
|
|
133
|
-
continue
|
|
134
|
-
|
|
135
|
-
if hasattr(current[key], "default_factory"):
|
|
136
|
-
default_value = current[key].default_factory()
|
|
137
|
-
else:
|
|
138
|
-
default_value = current[key]
|
|
139
|
-
current[key] = defaultdict(
|
|
140
|
-
lambda: default_value,
|
|
141
|
-
value,
|
|
142
|
-
)
|
|
143
94
|
|
|
144
95
|
merged = {}
|
|
145
96
|
for style in styles:
|
|
146
97
|
if isinstance(style, str):
|
|
147
98
|
style = get_style(style)
|
|
148
99
|
else:
|
|
149
|
-
|
|
100
|
+
sanitize_leaves(style)
|
|
150
101
|
unflatten_style(style)
|
|
151
|
-
|
|
102
|
+
sanitize_ambiguous(style)
|
|
103
|
+
update_style(style, merged)
|
|
152
104
|
|
|
153
105
|
return merged
|
|
154
106
|
|
iplotx/style/leaf_info.py
CHANGED
|
@@ -22,10 +22,12 @@ rotating_leaves = (
|
|
|
22
22
|
"vpadding",
|
|
23
23
|
"hmargin",
|
|
24
24
|
"vmargin",
|
|
25
|
-
"padding",
|
|
26
25
|
"ports",
|
|
27
26
|
"width",
|
|
28
27
|
"height",
|
|
28
|
+
"shrink",
|
|
29
|
+
# DEPRECATED
|
|
30
|
+
"padding",
|
|
29
31
|
)
|
|
30
32
|
|
|
31
33
|
# These properties are also terminal style properties, but they cannot be rotated.
|
iplotx/style/library.py
CHANGED
iplotx/tree.py
CHANGED
|
@@ -193,6 +193,10 @@ class TreeArtist(mpl.artist.Artist):
|
|
|
193
193
|
"""Get vertex or edge layout."""
|
|
194
194
|
layout_columns = [f"_ipx_layout_{i}" for i in range(self._ipx_internal_data["ndim"])]
|
|
195
195
|
|
|
196
|
+
# Equivalence vertex <-> node
|
|
197
|
+
if kind == "node":
|
|
198
|
+
kind = "vertex"
|
|
199
|
+
|
|
196
200
|
if kind == "vertex":
|
|
197
201
|
layout = self._ipx_internal_data["vertex_df"][layout_columns]
|
|
198
202
|
return layout
|
|
@@ -245,6 +249,10 @@ class TreeArtist(mpl.artist.Artist):
|
|
|
245
249
|
return bbox
|
|
246
250
|
|
|
247
251
|
def _get_label_series(self, kind: str) -> Optional[pd.Series]:
|
|
252
|
+
# Equivalence vertex <-> node
|
|
253
|
+
if kind == "node":
|
|
254
|
+
kind = "vertex"
|
|
255
|
+
|
|
248
256
|
if "label" in self._ipx_internal_data[f"{kind}_df"].columns:
|
|
249
257
|
return self._ipx_internal_data[f"{kind}_df"]["label"]
|
|
250
258
|
else:
|
|
@@ -254,6 +262,8 @@ class TreeArtist(mpl.artist.Artist):
|
|
|
254
262
|
"""Get VertexCollection artist."""
|
|
255
263
|
return self._vertices
|
|
256
264
|
|
|
265
|
+
get_nodes = get_vertices
|
|
266
|
+
|
|
257
267
|
def get_edges(self) -> EdgeCollection:
|
|
258
268
|
"""Get EdgeCollection artist."""
|
|
259
269
|
return self._edges
|
|
@@ -262,6 +272,8 @@ class TreeArtist(mpl.artist.Artist):
|
|
|
262
272
|
"""Get leaf VertexCollection artist."""
|
|
263
273
|
return self._leaf_vertices
|
|
264
274
|
|
|
275
|
+
get_leaf_nodes = get_leaf_vertices
|
|
276
|
+
|
|
265
277
|
def get_leaf_edges(self) -> Optional[LeafEdgeCollection]:
|
|
266
278
|
"""Get LeafEdgeCollection artist if present."""
|
|
267
279
|
if hasattr(self, "_leaf_edges"):
|
|
@@ -272,6 +284,8 @@ class TreeArtist(mpl.artist.Artist):
|
|
|
272
284
|
"""Get list of vertex label artists."""
|
|
273
285
|
return self._vertices.get_labels()
|
|
274
286
|
|
|
287
|
+
get_node_labels = get_vertex_labels
|
|
288
|
+
|
|
275
289
|
def get_edge_labels(self) -> LabelCollection:
|
|
276
290
|
"""Get list of edge label artists."""
|
|
277
291
|
return self._edges.get_labels()
|
iplotx/utils/style.py
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
from collections import defaultdict
|
|
3
3
|
|
|
4
|
+
from ..style.leaf_info import (
|
|
5
|
+
style_leaves,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import networkx as nx
|
|
10
|
+
except ImportError:
|
|
11
|
+
nx = None
|
|
12
|
+
|
|
4
13
|
|
|
5
14
|
def copy_with_deep_values(style):
|
|
6
15
|
"""Make a deep copy of the style dict but do not create copies of the keys."""
|
|
@@ -15,3 +24,105 @@ def copy_with_deep_values(style):
|
|
|
15
24
|
else:
|
|
16
25
|
newdict[key] = copy.copy(value)
|
|
17
26
|
return newdict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def sanitize_leaves(style: dict):
|
|
30
|
+
"""Sanitize the leaves of a style dictionary.
|
|
31
|
+
|
|
32
|
+
Parameters:
|
|
33
|
+
style (dict): A style dictionary.
|
|
34
|
+
Returns:
|
|
35
|
+
None: The style dictionary is modified in place.
|
|
36
|
+
"""
|
|
37
|
+
for key, value in style.items():
|
|
38
|
+
if key in style_leaves:
|
|
39
|
+
# Networkx has a few lazy data structures
|
|
40
|
+
# TODO: move this code to provider
|
|
41
|
+
if nx is not None:
|
|
42
|
+
if isinstance(value, nx.classes.reportviews.NodeView):
|
|
43
|
+
style[key] = dict(value)
|
|
44
|
+
elif isinstance(value, nx.classes.reportviews.EdgeViewABC):
|
|
45
|
+
style[key] = [v for *e, v in value]
|
|
46
|
+
|
|
47
|
+
elif isinstance(value, dict):
|
|
48
|
+
sanitize_leaves(value)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def update_style(new_style: dict, current_style: dict):
|
|
52
|
+
"""Update the current style with a new style.
|
|
53
|
+
|
|
54
|
+
Parameters:
|
|
55
|
+
new_style (dict): A new style dictionary.
|
|
56
|
+
current_style (dict): The current style dictionary.
|
|
57
|
+
Returns:
|
|
58
|
+
None: The current style dictionary is modified in place.
|
|
59
|
+
"""
|
|
60
|
+
for key, value in new_style.items():
|
|
61
|
+
if key not in current_style:
|
|
62
|
+
current_style[key] = value
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
# Style non-leaves are either recurred into or deleted
|
|
66
|
+
if key not in style_leaves:
|
|
67
|
+
if isinstance(value, dict):
|
|
68
|
+
update_style(value, current_style[key])
|
|
69
|
+
elif value is None:
|
|
70
|
+
del current_style[key]
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
f"Setting non-leaf style value to a non-dict: {key}, {value}",
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
# Style leaves could be incomplete, ensure a sensible default
|
|
77
|
+
if value is None:
|
|
78
|
+
del current_style[key]
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
if not isinstance(value, dict):
|
|
82
|
+
current_style[key] = value
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
if hasattr(value, "default_factory"):
|
|
86
|
+
current_style[key] = value
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
if hasattr(current_style[key], "default_factory"):
|
|
90
|
+
default_value = current_style[key].default_factory()
|
|
91
|
+
else:
|
|
92
|
+
default_value = current_style[key]
|
|
93
|
+
current_style[key] = defaultdict(
|
|
94
|
+
lambda: default_value,
|
|
95
|
+
value,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def sanitize_ambiguous(style: dict):
|
|
100
|
+
"""Fix a few ambiguous cases in the style dict.
|
|
101
|
+
|
|
102
|
+
Parameters:
|
|
103
|
+
style (dict): A style dictionary. This must be unflattened beforehand.
|
|
104
|
+
Returns:
|
|
105
|
+
None: The style dictionary is modified in place.
|
|
106
|
+
|
|
107
|
+
NOTE: This function exists by design, not accident. It is useful for purposeful
|
|
108
|
+
(e.g. node vs vertex) or historical (e.g. edge_padding vs edge_shrink) reasons.
|
|
109
|
+
Either way, it softens the user experience without complicating the API.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
# Accept "node" style as "vertex" style for user flexibility
|
|
113
|
+
if "node" in style:
|
|
114
|
+
style_node = style.pop("node")
|
|
115
|
+
if "vertex" not in style:
|
|
116
|
+
style["vertex"] = style_node
|
|
117
|
+
else:
|
|
118
|
+
# "node" style applies on TOP of "vertex" style
|
|
119
|
+
update_style(style_node, style["vertex"])
|
|
120
|
+
|
|
121
|
+
# NOTE: Deprecate edge_padding for edge_shrink
|
|
122
|
+
for edgekey in ["edge", "leafedge"]:
|
|
123
|
+
if "padding" in style.get(edgekey, {}):
|
|
124
|
+
# shrink takes over
|
|
125
|
+
if "shrink" in style[edgekey]:
|
|
126
|
+
del style[edgekey]["padding"]
|
|
127
|
+
else:
|
|
128
|
+
style[edgekey]["shrink"] = style[edgekey].pop("padding")
|
iplotx/version.py
CHANGED
iplotx/vertex.py
CHANGED
|
@@ -247,7 +247,11 @@ class VertexCollection(PatchCollection):
|
|
|
247
247
|
patches = []
|
|
248
248
|
sizes = []
|
|
249
249
|
for i, (vid, row) in enumerate(self._layout.iterrows()):
|
|
250
|
-
|
|
250
|
+
# Use vertex labels if present as a fallback key for style rotation
|
|
251
|
+
# This way one can be very specific via the exact object or cast
|
|
252
|
+
# a looser net with the label string
|
|
253
|
+
key2 = self._labels.iloc[i] if self._labels is not None else None
|
|
254
|
+
stylei = rotate_style(style, index=i, key=vid, key2=key2)
|
|
251
255
|
if stylei.get("size", 20) == "label":
|
|
252
256
|
stylei["size"] = _get_label_width_height(
|
|
253
257
|
str(self._labels[vid]), **style.get("label", {})
|
|
@@ -4,15 +4,15 @@ iplotx/cascades.py,sha256=OPqF7Huls-HFmDA5MCF6DEZlUeRVaXsbQcHBoKAgNJs,8182
|
|
|
4
4
|
iplotx/groups.py,sha256=_9KdIiTAi1kXtd2mDywgBJCbqoRq2z-5fzOPf76Wgb8,6287
|
|
5
5
|
iplotx/label.py,sha256=6am3a0ejcW_bWEXSOODE1Ke3AyCU1lJ45RfnXNbHAQw,8923
|
|
6
6
|
iplotx/layout.py,sha256=KxmRLqjo8AYCBAmXez8rIiLU2sM34qhb6ox9AHYwRyE,4839
|
|
7
|
-
iplotx/network.py,sha256=
|
|
7
|
+
iplotx/network.py,sha256=SGmXXrFxqgOoQcEJSZdL3WiI6rHDlPOGg5loGPrYpDk,11688
|
|
8
8
|
iplotx/plotting.py,sha256=IEUxW1xzTljLjBfsVP2BNsOPCDpj5bEPZ99bzvD5-mo,10066
|
|
9
|
-
iplotx/tree.py,sha256
|
|
9
|
+
iplotx/tree.py,sha256=TxbNoBHS0CfswrcMIWCNtnOl_3e4-PwCrVo0goywC0U,28807
|
|
10
10
|
iplotx/typing.py,sha256=QLdzV358IiD1CFe88MVp0D77FSx5sSAVUmM_2WPPE8I,1463
|
|
11
|
-
iplotx/version.py,sha256=
|
|
12
|
-
iplotx/vertex.py,sha256
|
|
13
|
-
iplotx/edge/__init__.py,sha256=
|
|
14
|
-
iplotx/edge/arrow.py,sha256=
|
|
15
|
-
iplotx/edge/geometry.py,sha256=
|
|
11
|
+
iplotx/version.py,sha256=tH4d2ah5uIPOD3rmxSuQMJ0PfIEEE0SOFEJKo0zwzXU,66
|
|
12
|
+
iplotx/vertex.py,sha256=hqdlD9fRBSwH5bRvlpaaPu7jgUR4z9nob1SYfPWDxtI,14966
|
|
13
|
+
iplotx/edge/__init__.py,sha256=AVnLsrDWWCkix1LVhrjpWKEKDxOp8joM4tF6RqEHC8I,27115
|
|
14
|
+
iplotx/edge/arrow.py,sha256=ZKt3UNZ7XRa2S3KxpoQfd4q_6eSUHOS476BZNqlf2pw,16462
|
|
15
|
+
iplotx/edge/geometry.py,sha256=wpFTi12-BaUaWr6Ie-nHV_SMAdSGJvjzJaqeEaSPf9w,15053
|
|
16
16
|
iplotx/edge/leaf.py,sha256=SyGMv2PIOoH0pey8-aMVaZheK3hNe1Qz_okcyWbc4E4,4268
|
|
17
17
|
iplotx/edge/ports.py,sha256=BpkbiEhX4mPBBAhOv4jcKFG4Y8hxXz5GRtVLCC0jbtI,1235
|
|
18
18
|
iplotx/ingest/__init__.py,sha256=S0YfnXcFKseB7ZBQc4yRt0cNDsLlhqdom0TmSY3OY2E,4756
|
|
@@ -26,13 +26,13 @@ iplotx/ingest/providers/tree/cogent3.py,sha256=JmELbDK7LyybiJzFNbmeqZ4ySJoDajvFf
|
|
|
26
26
|
iplotx/ingest/providers/tree/ete4.py,sha256=D7usSq0MOjzrk3EoLi834IlaDGwv7_qG6Qt0ptfKqfI,928
|
|
27
27
|
iplotx/ingest/providers/tree/simple.py,sha256=aV9wGqBomJ5klM_aJQeuL_Q_J1pLCv6AFN98BPDiKUw,2593
|
|
28
28
|
iplotx/ingest/providers/tree/skbio.py,sha256=O1KUr8tYi28pZ3VVjapgO4Uj-YpMuix3GhOH5je8Lv4,822
|
|
29
|
-
iplotx/style/__init__.py,sha256=
|
|
30
|
-
iplotx/style/leaf_info.py,sha256=
|
|
31
|
-
iplotx/style/library.py,sha256=
|
|
29
|
+
iplotx/style/__init__.py,sha256=rf1GutrE8hHUhCoe4FGKYX-aNtHuu_U-kYQnqUxZNrY,10282
|
|
30
|
+
iplotx/style/leaf_info.py,sha256=mcd6ewZl3jC0CPshmbeUkNp2geoihJW9515roGy2T8o,1000
|
|
31
|
+
iplotx/style/library.py,sha256=58Y8BlllGLsR4pQM7_PVCP5tH6_4GkchXZvJpqGHlcg,8534
|
|
32
32
|
iplotx/utils/geometry.py,sha256=6RrC6qaB0-1vIk1LhGA4CfsiMd-9JNniSPyL_l9mshE,9245
|
|
33
33
|
iplotx/utils/internal.py,sha256=WWfcZDGK8Ut1y_tOHRGg9wSqY1bwSeLQO7dHM_8Tvwo,107
|
|
34
34
|
iplotx/utils/matplotlib.py,sha256=wELE73quQv10-1w9uA5eDTgkZkylJvjg7pd3K5tZPOo,6294
|
|
35
|
-
iplotx/utils/style.py,sha256=
|
|
36
|
-
iplotx-0.
|
|
37
|
-
iplotx-0.
|
|
38
|
-
iplotx-0.
|
|
35
|
+
iplotx/utils/style.py,sha256=vyNP80nDYVinqm6_9ltCJCtjK35ZcGlHvOskNv3eQBc,4225
|
|
36
|
+
iplotx-0.8.0.dist-info/METADATA,sha256=L756OyuMisG3j2Y_OsgvcqUcCavwDbVAPvfwBg7S9dQ,4908
|
|
37
|
+
iplotx-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
38
|
+
iplotx-0.8.0.dist-info/RECORD,,
|
|
File without changes
|