iplotx 0.2.1__py3-none-any.whl → 0.3.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/tree.py CHANGED
@@ -3,12 +3,14 @@ from typing import (
3
3
  Sequence,
4
4
  )
5
5
  from collections.abc import Hashable
6
+ from collections import defaultdict
6
7
 
7
8
  import numpy as np
8
9
  import pandas as pd
9
10
  import matplotlib as mpl
10
11
 
11
12
  from .style import (
13
+ context,
12
14
  get_style,
13
15
  rotate_style,
14
16
  )
@@ -19,6 +21,7 @@ from .utils.matplotlib import (
19
21
  )
20
22
  from .ingest import (
21
23
  ingest_tree_data,
24
+ data_providers,
22
25
  )
23
26
  from .vertex import (
24
27
  VertexCollection,
@@ -30,6 +33,9 @@ from .edge import (
30
33
  from .label import (
31
34
  LabelCollection,
32
35
  )
36
+ from .cascades import (
37
+ CascadeCollection,
38
+ )
33
39
  from .network import (
34
40
  _update_from_internal,
35
41
  )
@@ -51,13 +57,14 @@ class TreeArtist(mpl.artist.Artist):
51
57
  def __init__(
52
58
  self,
53
59
  tree,
54
- layout="horizontal",
55
- orientation="right",
60
+ layout: Optional[str] = "horizontal",
61
+ orientation: Optional[str] = None,
56
62
  directed: bool | str = False,
57
63
  vertex_labels: Optional[
58
64
  bool | list[str] | dict[Hashable, str] | pd.Series
59
65
  ] = None,
60
- edge_labels: Optional[Sequence] = None,
66
+ edge_labels: Optional[Sequence | dict[Hashable, str] | pd.Series] = None,
67
+ leaf_labels: Optional[Sequence | dict[Hashable, str]] | pd.Series = None,
61
68
  transform: mpl.transforms.Transform = mpl.transforms.IdentityTransform(),
62
69
  offset_transform: Optional[mpl.transforms.Transform] = None,
63
70
  ):
@@ -73,6 +80,11 @@ class TreeArtist(mpl.artist.Artist):
73
80
  following choices: "parent" or "child".
74
81
  vertex_labels: Labels for the vertices. Can be a list, dictionary, or pandas Series.
75
82
  edge_labels: Labels for the edges. Can be a sequence of strings.
83
+ leaf_labels: Labels for the leaves. Can be a sequence of strings or a pandas Series.
84
+ These labels are positioned at the depth of the deepest leaf. If you want to
85
+ label leaves next to each leaf independently of how deep they are, use
86
+ the "vertex_labels" parameter instead - usually as a dict with the leaves
87
+ as keys and the labels as values.
76
88
  transform: The transform to apply to the tree artist. This is usually the identity.
77
89
  offset_transform: The offset transform to apply to the tree artist. This is
78
90
  usually `ax.transData`.
@@ -84,8 +96,10 @@ class TreeArtist(mpl.artist.Artist):
84
96
  layout,
85
97
  orientation=orientation,
86
98
  directed=directed,
99
+ layout_style=get_style(".layout", {}),
87
100
  vertex_labels=vertex_labels,
88
101
  edge_labels=edge_labels,
102
+ leaf_labels=leaf_labels,
89
103
  )
90
104
 
91
105
  super().__init__()
@@ -101,6 +115,12 @@ class TreeArtist(mpl.artist.Artist):
101
115
 
102
116
  self._add_vertices()
103
117
  self._add_edges()
118
+ self._add_leaf_vertices()
119
+
120
+ # NOTE: cascades need to be created after leaf vertices in case
121
+ # they are requested to wrap around them.
122
+ if "cascade" in self.get_vertices().get_style():
123
+ self._add_cascades()
104
124
 
105
125
  def get_children(self) -> tuple[mpl.artist.Artist]:
106
126
  """Get the children of this artist.
@@ -108,7 +128,12 @@ class TreeArtist(mpl.artist.Artist):
108
128
  Returns:
109
129
  The artists for vertices and edges.
110
130
  """
111
- return (self._vertices, self._edges)
131
+ children = [self._vertices, self._edges]
132
+ if hasattr(self, "_leaf_vertices"):
133
+ children.append(self._leaf_vertices)
134
+ if hasattr(self, "_cascades"):
135
+ children.append(self._cascades)
136
+ return tuple(children)
112
137
 
113
138
  def set_figure(self, fig) -> None:
114
139
  """Set the figure for this artist and its children.
@@ -120,6 +145,23 @@ class TreeArtist(mpl.artist.Artist):
120
145
  for child in self.get_children():
121
146
  child.set_figure(fig)
122
147
 
148
+ # At the end, if there are cadcades with extent depending on
149
+ # leaf edges, we should update them
150
+ self._update_cascades_extent()
151
+
152
+ def _update_cascades_extent(self) -> None:
153
+ """Update cascades if extent depends on leaf labels."""
154
+ if not hasattr(self, "_cascades"):
155
+ return
156
+
157
+ style_cascade = self.get_vertices().get_style()["cascade"]
158
+ extend_to_labels = style_cascade.get("extend", False) == "leaf_labels"
159
+ if not extend_to_labels:
160
+ return
161
+
162
+ maxdepth = self._get_maxdepth_leaf_labels()
163
+ self._cascades.set_maxdepth(maxdepth)
164
+
123
165
  def get_offset_transform(self):
124
166
  """Get the offset transform (for vertices/edges)."""
125
167
  return self._offset_transform
@@ -137,6 +179,16 @@ class TreeArtist(mpl.artist.Artist):
137
179
  if kind == "vertex":
138
180
  layout = self._ipx_internal_data["vertex_df"][layout_columns]
139
181
  return layout
182
+ elif kind == "leaf":
183
+ leaves = self._ipx_internal_data["leaf_df"].index
184
+ layout = self._ipx_internal_data["vertex_df"][layout_columns]
185
+ # NOTE: workaround for a pandas bug
186
+ idxs = []
187
+ for i, vid in enumerate(layout.index):
188
+ if vid in leaves:
189
+ idxs.append(i)
190
+ layout = layout.iloc[idxs]
191
+ return layout
140
192
 
141
193
  elif kind == "edge":
142
194
  return self._ipx_internal_data["edge_df"][layout_columns]
@@ -161,6 +213,14 @@ class TreeArtist(mpl.artist.Artist):
161
213
  edge_bbox = self._edges.get_datalim(transData)
162
214
  bbox = mpl.transforms.Bbox.union([bbox, edge_bbox])
163
215
 
216
+ if hasattr(self, "_cascades"):
217
+ cascades_bbox = self._cascades.get_datalim(transData)
218
+ bbox = mpl.transforms.Bbox.union([bbox, cascades_bbox])
219
+
220
+ if hasattr(self, "_leaf_vertices"):
221
+ leaf_labels_bbox = self._leaf_vertices.get_datalim(transData)
222
+ bbox = mpl.transforms.Bbox.union([bbox, leaf_labels_bbox])
223
+
164
224
  bbox = bbox.expanded(sw=(1.0 + pad), sh=(1.0 + pad))
165
225
  return bbox
166
226
 
@@ -178,6 +238,12 @@ class TreeArtist(mpl.artist.Artist):
178
238
  """Get EdgeCollection artist."""
179
239
  return self._edges
180
240
 
241
+ def get_leaf_vertices(self) -> Optional[VertexCollection]:
242
+ """Get leaf VertexCollection artist."""
243
+ if hasattr(self, "_leaf_vertices"):
244
+ return self._leaf_vertices
245
+ return None
246
+
181
247
  def get_vertex_labels(self) -> LabelCollection:
182
248
  """Get list of vertex label artists."""
183
249
  return self._vertices.get_labels()
@@ -186,6 +252,11 @@ class TreeArtist(mpl.artist.Artist):
186
252
  """Get list of edge label artists."""
187
253
  return self._edges.get_labels()
188
254
 
255
+ def get_leaf_labels(self) -> Optional[LabelCollection]:
256
+ if hasattr(self, "_leaf_vertices"):
257
+ return self._leaf_vertices.get_labels()
258
+ return None
259
+
189
260
  def _add_vertices(self) -> None:
190
261
  """Add vertices to the tree."""
191
262
  self._vertices = VertexCollection(
@@ -200,6 +271,101 @@ class TreeArtist(mpl.artist.Artist):
200
271
  offset_transform=self.get_offset_transform(),
201
272
  )
202
273
 
274
+ def _add_leaf_vertices(self) -> None:
275
+ """Add invisible deep vertices as leaf label anchors."""
276
+ leaf_layout = self.get_layout("leaf").copy()
277
+ # Set all to max depth
278
+ depth_idx = int(self._ipx_internal_data["layout_name"] == "vertical")
279
+ leaf_layout.iloc[:, depth_idx] = leaf_layout.iloc[:, depth_idx].max()
280
+
281
+ # Set invisible vertices with visible labels
282
+ layout_name = self._ipx_internal_data["layout_name"]
283
+ orientation = self._ipx_internal_data["orientation"]
284
+ if layout_name == "radial":
285
+ ha = "auto"
286
+ elif orientation in ("left", "ascending"):
287
+ ha = "right"
288
+ else:
289
+ ha = "left"
290
+
291
+ leaf_vertex_style = {
292
+ "size": 0,
293
+ "label": {
294
+ "verticalalignment": "center",
295
+ "horizontalalignment": ha,
296
+ "hmargin": 5,
297
+ "bbox": {
298
+ "facecolor": (1, 1, 1, 0),
299
+ },
300
+ },
301
+ }
302
+ with context({"vertex": leaf_vertex_style}):
303
+ leaf_vertex_style = get_style(".vertex")
304
+ self._leaf_vertices = VertexCollection(
305
+ layout=leaf_layout,
306
+ layout_coordinate_system=self._ipx_internal_data.get(
307
+ "layout_coordinate_system",
308
+ "catesian",
309
+ ),
310
+ style=leaf_vertex_style,
311
+ labels=self._get_label_series("leaf"),
312
+ transform=self.get_transform(),
313
+ offset_transform=self.get_offset_transform(),
314
+ )
315
+
316
+ def _add_cascades(self) -> None:
317
+ """Add cascade patches."""
318
+ # NOTE: If leaf labels are present and the cascades are requested to wrap around them,
319
+ # we have to compute the max extend of the cascades from the leaf labels.
320
+ maxdepth = None
321
+ style_cascade = self.get_vertices().get_style()["cascade"]
322
+ extend_to_labels = style_cascade.get("extend", False) == "leaf_labels"
323
+ has_leaf_labels = self.get_leaf_labels() is not None
324
+ if extend_to_labels and not has_leaf_labels:
325
+ raise ValueError("Cannot extend cascades: no leaf labels.")
326
+
327
+ if extend_to_labels and has_leaf_labels:
328
+ maxdepth = self._get_maxdepth_leaf_labels()
329
+
330
+ self._cascades = CascadeCollection(
331
+ tree=self.tree,
332
+ layout=self.get_layout(),
333
+ layout_name=self._ipx_internal_data["layout_name"],
334
+ orientation=self._ipx_internal_data["orientation"],
335
+ style=style_cascade,
336
+ provider=data_providers["tree"][self._ipx_internal_data["tree_library"]],
337
+ transform=self.get_offset_transform(),
338
+ maxdepth=maxdepth,
339
+ )
340
+
341
+ def _get_maxdepth_leaf_labels(self):
342
+ layout_name = self.get_layout_name()
343
+ if layout_name == "radial":
344
+ maxdepth = 0
345
+ # These are the text boxes, they must all be included
346
+ bboxes = self.get_leaf_labels().get_datalims_children(
347
+ self.get_offset_transform()
348
+ )
349
+ for bbox in bboxes:
350
+ r1 = np.linalg.norm([bbox.xmax, bbox.ymax])
351
+ r2 = np.linalg.norm([bbox.xmax, bbox.ymin])
352
+ r3 = np.linalg.norm([bbox.xmin, bbox.ymax])
353
+ r4 = np.linalg.norm([bbox.xmin, bbox.ymin])
354
+ maxdepth = max(maxdepth, r1, r2, r3, r4)
355
+ else:
356
+ orientation = self.get_orientation()
357
+ bbox = self.get_leaf_labels().get_datalim(self.get_offset_transform())
358
+ if (layout_name, orientation) == ("horizontal", "right"):
359
+ maxdepth = bbox.xmax
360
+ elif layout_name == "horizontal":
361
+ maxdepth = bbox.xmin
362
+ elif (layout_name, orientation) == ("vertical", "descending"):
363
+ maxdepth = bbox.ymin
364
+ elif layout_name == "vertical":
365
+ maxdepth = bbox.ymax
366
+
367
+ return maxdepth
368
+
203
369
  def _add_edges(self) -> None:
204
370
  """Add edges to the network artist.
205
371
 
@@ -259,7 +425,7 @@ class TreeArtist(mpl.artist.Artist):
259
425
  if layout_name == "horizontal":
260
426
  waypointsi = "x0y1"
261
427
  elif layout_name == "vertical":
262
- waypointsi = "y0y0"
428
+ waypointsi = "y0x1"
263
429
  elif layout_name == "radial":
264
430
  waypointsi = "r0a1"
265
431
  else:
@@ -296,17 +462,32 @@ class TreeArtist(mpl.artist.Artist):
296
462
  if "cmap" in edge_style:
297
463
  self._edges.set_array(colorarray)
298
464
 
465
+ def get_layout_name(self) -> str:
466
+ """Get the layout name."""
467
+ return self._ipx_internal_data["layout_name"]
468
+
469
+ def get_orientation(self) -> Optional[str]:
470
+ """Get the orientation of the tree layout."""
471
+ return self._ipx_internal_data.get("orientation", None)
472
+
299
473
  @_stale_wrapper
300
474
  def draw(self, renderer) -> None:
301
475
  """Draw each of the children, with some buffering mechanism."""
302
476
  if not self.get_visible():
303
477
  return
304
478
 
305
- # FIXME: Callbacks on stale vertices/edges??
479
+ # At the end, if there are cadcades with extent depending on
480
+ # leaf edges, we should update them
481
+ self._update_cascades_extent()
306
482
 
307
483
  # NOTE: looks like we have to manage the zorder ourselves
308
- # this is kind of funny actually
484
+ # this is kind of funny actually. Btw we need to ensure
485
+ # that cascades are drawn behind (earlier than) vertices
486
+ # and edges at equal zorder because it looks better that way.
487
+ z_suborder = defaultdict(int)
488
+ if hasattr(self, "_cascades"):
489
+ z_suborder[self._cascades] = -1
309
490
  children = list(self.get_children())
310
- children.sort(key=lambda x: x.zorder)
491
+ children.sort(key=lambda x: (x.zorder, z_suborder[x]))
311
492
  for art in children:
312
493
  art.draw(renderer)
iplotx/version.py CHANGED
@@ -2,4 +2,4 @@
2
2
  iplotx version information module.
3
3
  """
4
4
 
5
- __version__ = "0.2.1"
5
+ __version__ = "0.3.0"
iplotx/vertex.py CHANGED
@@ -104,6 +104,7 @@ class VertexCollection(PatchCollection):
104
104
  """Set the figure for this artist and all children."""
105
105
  super().set_figure(fig)
106
106
  self.set_sizes(self._sizes, self.get_figure(root=True).dpi)
107
+ self._update_children()
107
108
  for child in self.get_children():
108
109
  child.set_figure(fig)
109
110
 
@@ -209,6 +210,10 @@ class VertexCollection(PatchCollection):
209
210
  self._update_offsets_from_layout()
210
211
  self.stale = True
211
212
 
213
+ def get_style(self) -> Optional[dict[str, Any]]:
214
+ """Get the style dictionary for the vertices."""
215
+ return self._style
216
+
212
217
  def _init_vertex_patches(self):
213
218
  style = self._style or {}
214
219
  if "cmap" in style:
@@ -231,13 +236,11 @@ class VertexCollection(PatchCollection):
231
236
  patches = []
232
237
  sizes = []
233
238
  for i, (vid, row) in enumerate(self._layout.iterrows()):
234
- if style.get("size", 20) == "label":
235
- # NOTE: it's ok to overwrite the dict here
236
- style["size"] = _get_label_width_height(
239
+ stylei = rotate_style(style, index=i, key=vid)
240
+ if stylei.get("size", 20) == "label":
241
+ stylei["size"] = _get_label_width_height(
237
242
  str(self._labels[vid]), **style.get("label", {})
238
243
  )
239
-
240
- stylei = rotate_style(style, index=i, key=vid)
241
244
  if cmap_fun is not None:
242
245
  colorarray.append(style["facecolor"])
243
246
  stylei["facecolor"] = cmap_fun(stylei["facecolor"])
@@ -299,6 +302,30 @@ class VertexCollection(PatchCollection):
299
302
  if val and hasattr(self, "stale_callback_post"):
300
303
  self.stale_callback_post(self)
301
304
 
305
+ def _update_children(self) -> None:
306
+ """Update children before drawing and before first render."""
307
+ self._update_labels()
308
+
309
+ def _update_labels(self) -> None:
310
+ """Update labels before drawing.
311
+
312
+ NOTE: This needs to work in figure coordinates.
313
+ """
314
+ if not hasattr(self, "_label_collection"):
315
+ return
316
+
317
+ if self.get_layout_coordinate_system() != "polar":
318
+ return
319
+
320
+ transform = self.get_offset_transform()
321
+ trans = transform.transform
322
+
323
+ zero_fig = trans(np.array([0, 0]))
324
+ offsets_fig = trans(self.get_labels().get_offsets())
325
+ doffsets_fig = offsets_fig - zero_fig
326
+ rotations = np.arctan2(doffsets_fig[:, 1], doffsets_fig[:, 0])
327
+ self.get_labels().set_rotations(rotations)
328
+
302
329
  @mpl.artist.allow_rasterization
303
330
  def draw(self, renderer):
304
331
  if not self.get_visible():
@@ -311,6 +338,9 @@ class VertexCollection(PatchCollection):
311
338
 
312
339
  self.set_sizes(self._sizes, self.get_figure(root=True).dpi)
313
340
 
341
+ # Set the label rotations already, hopefully this is not too early
342
+ self._update_children()
343
+
314
344
  # NOTE: This draws the vertices first, then the labels.
315
345
  # The correct order would be vertex1->label1->vertex2->label2, etc.
316
346
  # We might fix if we manage to find a way to do it.
@@ -320,10 +350,12 @@ class VertexCollection(PatchCollection):
320
350
 
321
351
 
322
352
  def make_patch(
323
- marker: str, size: float | Sequence[float], **kwargs
353
+ marker: str = "o",
354
+ size: float | Sequence[float] = 20,
355
+ **kwargs,
324
356
  ) -> tuple[Patch, float]:
325
357
  """Make a patch of the given marker shape and size."""
326
- forbidden_props = ["label", "cmap", "norm"]
358
+ forbidden_props = ["label", "cmap", "norm", "cascade"]
327
359
  for prop in forbidden_props:
328
360
  if prop in kwargs:
329
361
  kwargs.pop(prop)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iplotx
3
- Version: 0.2.1
3
+ Version: 0.3.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
@@ -79,6 +79,7 @@ See [gallery](https://iplotx.readthedocs.io/en/latest/gallery/index.html).
79
79
  - Efficient plotting of large graphs using matplotlib's collection functionality. ✅
80
80
  - Support editing plotting elements after the plot is created, e.g. changing node colors, labels, etc. ✅
81
81
  - Support animations, e.g. showing the evolution of a network over time. ✅
82
+ - Support mouse interaction, e.g. hovering over or clicking on nodes and edges to get information about them. ✅
82
83
  - Support trees from special libraries such as ete3, biopython, etc. This will need a dedicated function and layouting. ✅
83
84
  - Support uni- and bi-directional communication between graph object and plot object.🏗️
84
85
 
@@ -0,0 +1,32 @@
1
+ iplotx/__init__.py,sha256=DIOWUaEo9vgmz8B6hBlurV95BEYmjmUe5BU-jRWrtG4,418
2
+ iplotx/cascades.py,sha256=_J8kxCHJrZmPKaN10e0uROAPsdcLrw41hzozZu1hQcg,8405
3
+ iplotx/groups.py,sha256=H3zIaqlILQaC6Iqgxrf3zSuRVgynQTLLEuuGy6lYgLI,6388
4
+ iplotx/label.py,sha256=m2ryXMh29QFLwdstRphnOPocW_Wnqir9WQscxWN1Z2E,7347
5
+ iplotx/layout.py,sha256=0Mqrs0odQ8Jk4nITvAYgayR6KDm3e-Lq1BY_DKeU3ok,4664
6
+ iplotx/network.py,sha256=rLVfC52PBiTR0I-CCvFPE0dj7t-KCLZwXSSJrpPVzh8,9719
7
+ iplotx/plotting.py,sha256=VU1LYp9peqLa1CmvHjF98BYxezkWUrcCWvd2MPB9n_8,7381
8
+ iplotx/style.py,sha256=NtrKYhTIkf7GPpepsROUbXxbodIc8gu066CdLNAsMQM,12009
9
+ iplotx/tree.py,sha256=YU3UoGV_Zxf_KkP4iIsi09WsL1vBvS3URkG-Uqzqyp4,18393
10
+ iplotx/typing.py,sha256=17TS-EJshNNOdeUC9mmJALiJ3gwI1nRq6AGlVdQRp1E,1384
11
+ iplotx/version.py,sha256=YK2iFN0pBkof4-yJ6Yzz65Uy4liSHJ30ixvRZVehhho,66
12
+ iplotx/vertex.py,sha256=JCXv-l934jDGdS2fOigOdDKDZ-5Az-7MUqHcQk8Jl18,12715
13
+ iplotx/edge/__init__.py,sha256=W68WienFUneWQ0iVTFGNm8KIBQGvkfXlYq2Kx1lDzU8,23076
14
+ iplotx/edge/arrow.py,sha256=2mF_HwhHkXir3lOYOTp8HW__PQgWRxaHXZN9mvk3hLQ,11452
15
+ iplotx/edge/geometry.py,sha256=7RwtIUOjz6pd1QTUaNwtIivnaM4ZfxVgMjTg1bIqqQA,12827
16
+ iplotx/edge/ports.py,sha256=pno3A7QdQyZGyQJOHVLYszBBeQAh-q2-jkd-lOe31TM,1183
17
+ iplotx/ingest/__init__.py,sha256=dEbdb3LP3QnPKpwA7y6fJl9spCWkCYYtXCQPCGA9oVk,4971
18
+ iplotx/ingest/heuristics.py,sha256=_ZSC9EiCr-eURmhGdufdZdyERpzPrPvRj01nv0rXkiE,6579
19
+ iplotx/ingest/typing.py,sha256=cPEvOT5yjDiXj6Y9E3ShH2uDmepn7YtPtBIZ_cOEp6g,10345
20
+ iplotx/ingest/providers/network/igraph.py,sha256=lCWIlKd2VwxJLoc2W4nivwkrvFjhmXhG9r_sJkFXWh0,2794
21
+ iplotx/ingest/providers/network/networkx.py,sha256=-lb_8jpq7DvoM9lWn43qL6jTqS-VqtpJEgcSJeFz7yQ,4261
22
+ iplotx/ingest/providers/tree/biopython.py,sha256=JFuD7NWV7j-fWt-6q29ApvlKf7Mqrnr_6wSkzSK-e38,1080
23
+ iplotx/ingest/providers/tree/cogent3.py,sha256=LHAFLronzlrlLWl79abXWOoFP-agIdzoQqj6O15xwLE,873
24
+ iplotx/ingest/providers/tree/ete4.py,sha256=vHwfYRazruR7eZfUgEJiSyeUA7UVBsTtR998uoL5bDQ,971
25
+ iplotx/ingest/providers/tree/skbio.py,sha256=fwlITpl71gIKDDoJKB6nmlC-J6A2fXoplX6ul5qXsYc,869
26
+ iplotx/utils/geometry.py,sha256=K5ZBYPmz4-KNm64pDh2p0L6PF5-u57SCVaEd2eWeRv0,8956
27
+ iplotx/utils/internal.py,sha256=WWfcZDGK8Ut1y_tOHRGg9wSqY1bwSeLQO7dHM_8Tvwo,107
28
+ iplotx/utils/matplotlib.py,sha256=T37SMwKNSA-dRKgNkVw5H4fGr5NACtxvBPebDJGdhjk,4516
29
+ iplotx/utils/style.py,sha256=fEi14nc37HQqHxZTPeQaTnFFwbSneY1oDCyv2UbWfKk,22
30
+ iplotx-0.3.0.dist-info/METADATA,sha256=7wB_lF97KjI_w1UAySphid4gt1bRCr5gGZ0YoPMqBW0,3809
31
+ iplotx-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
32
+ iplotx-0.3.0.dist-info/RECORD,,
@@ -1,31 +0,0 @@
1
- iplotx/__init__.py,sha256=DIOWUaEo9vgmz8B6hBlurV95BEYmjmUe5BU-jRWrtG4,418
2
- iplotx/groups.py,sha256=H3zIaqlILQaC6Iqgxrf3zSuRVgynQTLLEuuGy6lYgLI,6388
3
- iplotx/label.py,sha256=j-OjOX1R7VHh3Ihg4bhHaauxS4fuVHhGcHKoETAnUt4,5308
4
- iplotx/layout.py,sha256=RLCDgG9eO3mtZH-H3ETJFpvPR0uVl-xMUsgC4mcepWE,3930
5
- iplotx/network.py,sha256=rLVfC52PBiTR0I-CCvFPE0dj7t-KCLZwXSSJrpPVzh8,9719
6
- iplotx/plotting.py,sha256=1C6wenDeUohoYVl_vAcTs2aR-5LcRHFOcNb4ghIbkLI,7152
7
- iplotx/style.py,sha256=CmtycJKndmh_w28s4SpBJECyaAn8pQkfP244dMsAvv0,11399
8
- iplotx/tree.py,sha256=pu3dyIPsdX4iPJOSk_J5-IPajkCBkmUhEhp0jXzurtw,10695
9
- iplotx/typing.py,sha256=17TS-EJshNNOdeUC9mmJALiJ3gwI1nRq6AGlVdQRp1E,1384
10
- iplotx/version.py,sha256=RKuLXcXQo3HLVM55AR--Vu2Gmk8BKgdvxPEqTMqIa1E,66
11
- iplotx/vertex.py,sha256=q5B2_JUsGGszUh6OJ_ddr6uBvNmrxigolI0d5C-MThc,11667
12
- iplotx/edge/__init__.py,sha256=uM7YTr7YDbLIhAjUjfF7xT52LuE2CMj-erPeq68roxI,22227
13
- iplotx/edge/arrow.py,sha256=2mF_HwhHkXir3lOYOTp8HW__PQgWRxaHXZN9mvk3hLQ,11452
14
- iplotx/edge/geometry.py,sha256=db-k6HFYb_h6rWWxaD32tKefvRUIb7DLi1Y2x5vhgMA,10849
15
- iplotx/edge/ports.py,sha256=pno3A7QdQyZGyQJOHVLYszBBeQAh-q2-jkd-lOe31TM,1183
16
- iplotx/ingest/__init__.py,sha256=75Pml7X65tP8b2G3qaeZUdnDgwP6dclcCEEFl0BYSdo,4707
17
- iplotx/ingest/heuristics.py,sha256=32AZ8iidM_uooaBKe2EMjNU_nJDbhA_28ADc0dpQy5A,6636
18
- iplotx/ingest/typing.py,sha256=QEgCpLyfp-0v9czn6OaJ0_nuCo1AXv3lGS3aD6w-Ezw,3134
19
- iplotx/ingest/providers/network/igraph.py,sha256=_J7lH-jrT0_1oSfwgT_mQMRhxNoFwHo8dyBLCbgqETQ,2766
20
- iplotx/ingest/providers/network/networkx.py,sha256=u7NegapWZ0gWUj5n1PUVD-zZ92lKUiv6BLNTNIrXlRk,4233
21
- iplotx/ingest/providers/tree/biopython.py,sha256=7ZVD_WwIaBOSl-at4r_Y4d2qHQq6BcvlV-yDxMVCWnw,3456
22
- iplotx/ingest/providers/tree/cogent3.py,sha256=5O92zkdA43LWQ7h6r-uG_5X76EPHo-Yx9ODLb0hd_qc,3636
23
- iplotx/ingest/providers/tree/ete4.py,sha256=sGm2363yLJWsWsrRn2iFx8qRiq57h_5dQWBD41xJmhY,3660
24
- iplotx/ingest/providers/tree/skbio.py,sha256=iNZhY_TNLpzd55cK2nybjiftUfakeOrDDJ9dMBna_io,3629
25
- iplotx/utils/geometry.py,sha256=K5ZBYPmz4-KNm64pDh2p0L6PF5-u57SCVaEd2eWeRv0,8956
26
- iplotx/utils/internal.py,sha256=WWfcZDGK8Ut1y_tOHRGg9wSqY1bwSeLQO7dHM_8Tvwo,107
27
- iplotx/utils/matplotlib.py,sha256=T37SMwKNSA-dRKgNkVw5H4fGr5NACtxvBPebDJGdhjk,4516
28
- iplotx/utils/style.py,sha256=fEi14nc37HQqHxZTPeQaTnFFwbSneY1oDCyv2UbWfKk,22
29
- iplotx-0.2.1.dist-info/METADATA,sha256=_xHHXy4GbmnU5phRruUTbr1XlBtCJnOOyq5PLgv-m1k,3695
30
- iplotx-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
- iplotx-0.2.1.dist-info/RECORD,,
File without changes