iplotx 0.6.7__py3-none-any.whl → 0.7.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 CHANGED
@@ -352,9 +352,9 @@ class EdgeCollection(mpl.collections.PatchCollection):
352
352
  tension = 0
353
353
  ports = None
354
354
 
355
- # Scale padding by dpi
355
+ # Scale shrink by dpi
356
356
  dpi = self.figure.dpi if hasattr(self, "figure") else 72.0
357
- padding = dpi / 72.0 * edge_stylei.pop("padding", 0)
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
- padding=padding,
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(marker: str = "|>", width: float = 8, **kwargs):
153
- """Make a patch of the given marker shape and size."""
154
- height = kwargs.pop("height", width * 1.3)
155
- if height == "width":
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, padding=0):
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
- # Padding (assuming dpi scaling is already applied to the padding)
109
- ve += padding * np.array([np.cos(theta), np.sin(theta)])
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
- padding=0,
148
+ shrink=0,
149
149
  ):
150
150
  # Shorten at starting angle
151
- start = _get_shorter_edge_coords(vpath, vsize, angle1, padding) + vcoord_fig
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, padding) + vcoord_fig
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
- padding: float = 0,
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, padding) + vcoord_fig[0]
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, padding) + vcoord_fig[1]
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
- padding: float = 0,
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], padding)
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], padding)
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, padding) + vcoord_fig[i]
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
- padding: float = 0,
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], padding) + vcoord_fig[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/style/__init__.py CHANGED
@@ -141,6 +141,27 @@ def merge_styles(
141
141
  value,
142
142
  )
143
143
 
144
+ def _sanitize_ambiguous(style: dict):
145
+ """Fix a few ambiguous cases in the style dict."""
146
+
147
+ # Accept "node" style as "vertex" style for user flexibility
148
+ if "node" in style:
149
+ style_node = style.pop("node")
150
+ if "vertex" not in style:
151
+ style["vertex"] = style_node
152
+ else:
153
+ # "node" style applies on TOP of "vertex" style
154
+ _update(style_node, style["vertex"])
155
+
156
+ # NOTE: Deprecate edge_padding for edge_shrink
157
+ for edgekey in ["edge", "leafedge"]:
158
+ if "padding" in style.get(edgekey, {}):
159
+ # shrink takes over
160
+ if "shrink" in style[edgekey]:
161
+ del style[edgekey]["padding"]
162
+ else:
163
+ style[edgekey]["shrink"] = style[edgekey].pop("padding")
164
+
144
165
  merged = {}
145
166
  for style in styles:
146
167
  if isinstance(style, str):
@@ -148,6 +169,7 @@ def merge_styles(
148
169
  else:
149
170
  _sanitize_leaves(style)
150
171
  unflatten_style(style)
172
+ _sanitize_ambiguous(style)
151
173
  _update(style, merged)
152
174
 
153
175
  return merged
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
@@ -62,7 +62,7 @@ style_library = {
62
62
  "feedback": {
63
63
  "edge": {
64
64
  "linewidth": 4,
65
- "padding": 10,
65
+ "shrink": 10,
66
66
  "arrow": {
67
67
  "marker": ")>",
68
68
  "width": 20,
iplotx/tree.py CHANGED
@@ -311,13 +311,21 @@ class TreeArtist(mpl.artist.Artist):
311
311
  if not leaf_style.get("deep", True):
312
312
  return
313
313
 
314
+ # Given the conditions above, we should have leaf labels. If not,
315
+ # make a None series with a valid index
316
+ leaf_label_series = self._get_label_series("leaf")
317
+ if leaf_label_series is None:
318
+ leaf_label_series = self._ipx_internal_data["leaf_df"].copy()
319
+ leaf_label_series["label"] = None
320
+ leaf_label_series = leaf_label_series["label"]
321
+
314
322
  edge_style = get_style(
315
323
  ".leafedge",
316
324
  )
317
325
  default_style = {
318
326
  "linestyle": "--",
319
327
  "linewidth": 1,
320
- "edgecolor": "#111",
328
+ "color": "#111",
321
329
  }
322
330
  for key, value in default_style.items():
323
331
  if key not in edge_style:
@@ -335,14 +343,24 @@ class TreeArtist(mpl.artist.Artist):
335
343
  else:
336
344
  cmap_fun = None
337
345
 
338
- leaf_shallow_layout = self.get_layout("leaf")
339
-
340
346
  if "cmap" in edge_style:
341
347
  colorarray = []
342
348
  edgepatches = []
343
349
  adjacent_vertex_ids = []
344
- for i, vid in enumerate(leaf_shallow_layout.index):
345
- edge_stylei = rotate_style(edge_style, index=i, key=vid)
350
+ for i, (vid, label) in enumerate(leaf_label_series.items()):
351
+ # Use leaf label to compute backup key for style rotation
352
+ # NOTE: This is quite a common use case. Users typically
353
+ # refer to leaves via their labels because that's what you see
354
+ # on screen. While an object exact match should take priority
355
+ # for power users, a backup based on label is useful. Beginners
356
+ # won't stumble upon this anyway since it's only used if
357
+ # a dict-like property for leaf edges is provided, which is a
358
+ # decently advanced thing to do.
359
+ # NOTE: Multiple leaves might have the same label, in which case
360
+ # all leaves will be style matched to this *unless* the dict-like
361
+ # style object *also* matches on leaf objects directly, which
362
+ # takes precedence since key2 is only a fallback.
363
+ edge_stylei = rotate_style(edge_style, index=i, key=vid, key2=label)
346
364
 
347
365
  if cmap_fun is not None:
348
366
  colorarray.append(edge_stylei["color"])
iplotx/version.py CHANGED
@@ -2,4 +2,4 @@
2
2
  iplotx version information module.
3
3
  """
4
4
 
5
- __version__ = "0.6.7"
5
+ __version__ = "0.7.0"
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
- stylei = rotate_style(style, index=i, key=vid)
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", {})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iplotx
3
- Version: 0.6.7
3
+ Version: 0.7.0
4
4
  Summary: Plot networkx from igraph and networkx.
5
5
  Project-URL: Homepage, https://github.com/fabilab/iplotx
6
6
  Project-URL: Documentation, https://readthedocs.org/iplotx
@@ -6,13 +6,13 @@ iplotx/label.py,sha256=6am3a0ejcW_bWEXSOODE1Ke3AyCU1lJ45RfnXNbHAQw,8923
6
6
  iplotx/layout.py,sha256=KxmRLqjo8AYCBAmXez8rIiLU2sM34qhb6ox9AHYwRyE,4839
7
7
  iplotx/network.py,sha256=LaW9zQZ4sKiDVb25_icnquGNnN7HrKC7NO07o6PSmGI,11527
8
8
  iplotx/plotting.py,sha256=IEUxW1xzTljLjBfsVP2BNsOPCDpj5bEPZ99bzvD5-mo,10066
9
- iplotx/tree.py,sha256=Zzz7nCPZrSjh9_yHXFdd8hjbF-FYURTZs00rUHc4OT8,27304
9
+ iplotx/tree.py,sha256=-69lpPjisRfigY4fDQcmVxzOAf1tGwMXviUcW6mZU6U,28508
10
10
  iplotx/typing.py,sha256=QLdzV358IiD1CFe88MVp0D77FSx5sSAVUmM_2WPPE8I,1463
11
- iplotx/version.py,sha256=1Mi-AUAfWTkSNROfIn7bWkeZBzSl_j_A7HmbA4LSs0c,66
12
- iplotx/vertex.py,sha256=-JZaQPjIAWWP8Wap1HyR4g4HXLNGLwjf4v6jyo994Tk,14671
13
- iplotx/edge/__init__.py,sha256=HlxeIs88RbRrTetJNLcoq9gjV7cBhOdqQoiVZFFylFc,27081
14
- iplotx/edge/arrow.py,sha256=C4XoHGCYou1z2alz5Q2VhdaWYEzgebtEF70zVYY_frk,15533
15
- iplotx/edge/geometry.py,sha256=tiaF4PzvsNBoROrEgcCsw0YdxxZr3oBxF4ord_k4ThA,15069
11
+ iplotx/version.py,sha256=8BIMMcy91d9_BPlUuoNyMNeyj04-gejk1STd-SLsrT0,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=XMkQZ1U63wVNo98Zo5uJAn-uQgW2OTZABAizJqiuB3s,12253
30
- iplotx/style/leaf_info.py,sha256=JoX1cPjRM_k3f93jzUPQ3gPlVP4wY_n032nOVhrgelU,969
31
- iplotx/style/library.py,sha256=wO-eeY3EZfAl0v21aX9f5_MiZhHuL2kGsBYA3uJkIGs,8535
29
+ iplotx/style/__init__.py,sha256=_LhOJ9WJzC3of_nbaJyAJX3k4paI8utrigTzYDZRSB8,13138
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
35
  iplotx/utils/style.py,sha256=wMWxJykxBD-JmcN8-rSKlWcV6pMfwKgR4EzSpk_NX8k,547
36
- iplotx-0.6.7.dist-info/METADATA,sha256=O3tkLhtN4pM3FxyONC75a3CYvf1Ba6iU8bHRTgtBzgM,4908
37
- iplotx-0.6.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
- iplotx-0.6.7.dist-info/RECORD,,
36
+ iplotx-0.7.0.dist-info/METADATA,sha256=vbdamou2U35GeUBn4_fG8kzeC9fTwYRzKeg2Ik9212Q,4908
37
+ iplotx-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ iplotx-0.7.0.dist-info/RECORD,,
File without changes