tree-annotated-plot 0.3.0__py3-none-any.whl → 0.4.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.
@@ -68,6 +68,15 @@ class PlotConfig:
68
68
  "(x-encoded).",
69
69
  ] = None
70
70
 
71
+ spacing: Annotated[
72
+ int,
73
+ "Pixels of blank space between the tree panel and the chart panel. "
74
+ "Default 0, which butts the two together. Distinct from "
75
+ "`shift_tree_loc`, which moves the tree within its own panel by "
76
+ "resizing the label strip; `spacing` sets the gap between the panels "
77
+ "and applies whether or not `connect_leader_to_label` is on.",
78
+ ] = 0
79
+
71
80
  tree_line_width: Annotated[
72
81
  float,
73
82
  "Stroke width (px) for the tree's branch lines. Default 2.",
@@ -155,8 +164,9 @@ class PlotConfig:
155
164
  shift_tree_loc: Annotated[
156
165
  int,
157
166
  "Pixels by which to shift the tree toward (positive) or away from "
158
- "(negative) the chart. Default 0. Has no effect when "
159
- "connect_leader_to_label is off.",
167
+ "(negative) the chart, by resizing the label strip inside the tree "
168
+ "panel. Default 0. Has no effect when connect_leader_to_label is off; "
169
+ "to set the gap between the tree and chart panels use `spacing`.",
160
170
  ] = 0
161
171
 
162
172
  color_tree_by: Annotated[
@@ -49,6 +49,7 @@ def plot(
49
49
  branch_length: Literal["div", "num_date"],
50
50
  tree_size: int = 100,
51
51
  tree_location: TreeLocation | None = None,
52
+ spacing: int = 0,
52
53
  tree_line_width: float = 2.0,
53
54
  tree_node_size: float = 45,
54
55
  leader_line_width: float = 1.0,
@@ -77,6 +78,7 @@ def plot(
77
78
  branch_length=branch_length,
78
79
  tree_size=tree_size,
79
80
  tree_location=tree_location,
81
+ spacing=spacing,
80
82
  tree_line_width=tree_line_width,
81
83
  tree_node_size=tree_node_size,
82
84
  leader_line_width=leader_line_width,
@@ -281,7 +283,10 @@ def _build(
281
283
  hoisted_config, hoisted_other = _pop_toplevel_only_attrs(new_chart)
282
284
 
283
285
  combined = _concat_for_location(
284
- tree_chart=tree_chart, user_chart=new_chart, location=location
286
+ tree_chart=tree_chart,
287
+ user_chart=new_chart,
288
+ location=location,
289
+ spacing=config.spacing,
285
290
  )
286
291
  _apply_combined_config(combined, hoisted_config)
287
292
  for k, v in hoisted_other.items():
@@ -320,6 +325,7 @@ def _concat_for_location(
320
325
  tree_chart: alt.TopLevelMixin,
321
326
  user_chart: alt.TopLevelMixin,
322
327
  location: TreeLocation,
328
+ spacing: int,
323
329
  ) -> alt.HConcatChart | alt.VConcatChart:
324
330
  """Concat tree and chart in the order implied by the tree's location.
325
331
 
@@ -333,19 +339,19 @@ def _concat_for_location(
333
339
  user-chart marks whose color values aren't in the tree's domain.
334
340
  """
335
341
  if location == "left":
336
- return alt.hconcat(tree_chart, user_chart, spacing=0).resolve_scale(
342
+ return alt.hconcat(tree_chart, user_chart, spacing=spacing).resolve_scale(
337
343
  y="independent", color="independent"
338
344
  )
339
345
  if location == "right":
340
- return alt.hconcat(user_chart, tree_chart, spacing=0).resolve_scale(
346
+ return alt.hconcat(user_chart, tree_chart, spacing=spacing).resolve_scale(
341
347
  y="independent", color="independent"
342
348
  )
343
349
  if location == "top":
344
- return alt.vconcat(tree_chart, user_chart, spacing=0).resolve_scale(
350
+ return alt.vconcat(tree_chart, user_chart, spacing=spacing).resolve_scale(
345
351
  x="independent", color="independent"
346
352
  )
347
353
  if location == "bottom":
348
- return alt.vconcat(user_chart, tree_chart, spacing=0).resolve_scale(
354
+ return alt.vconcat(user_chart, tree_chart, spacing=spacing).resolve_scale(
349
355
  x="independent", color="independent"
350
356
  )
351
357
  raise ValueError(f"unreachable: tree_location={location!r}")
@@ -1475,6 +1481,11 @@ def _build_scale_bar_layer(
1475
1481
 
1476
1482
  _LABEL_PAD_PX_MIN = 4
1477
1483
  _LABEL_PAD_RATIO = 0.4 # `LABEL_PAD_PX = max(MIN, font_size * RATIO)`
1484
+ # Gap between the labels and the chart, kept separate from the tree-facing pad
1485
+ # above because it clears the chart's frame rather than the deepest tip, and so
1486
+ # wants a small fixed clearance rather than the pad's larger, faster-growing one.
1487
+ _LABEL_CHART_GAP_PX_MIN = 3
1488
+ _LABEL_CHART_GAP_RATIO = 0.2 # `LABEL_CHART_GAP_PX = max(MIN, font_size * RATIO)`
1478
1489
  _LABEL_CHAR_PX_RATIO = 0.6 # rough proportional sans-serif glyph-width estimate
1479
1490
  _LABEL_HALO_RATIO = 0.6 # white-halo strokeWidth as a fraction of font_size
1480
1491
 
@@ -1614,9 +1625,15 @@ def _build_tree_chart(
1614
1625
  max_name_len = max((len(n) for n in names), default=0)
1615
1626
  char_px = strain_label_font_size * _LABEL_CHAR_PX_RATIO
1616
1627
  label_pad_px = max(_LABEL_PAD_PX_MIN, strain_label_font_size * _LABEL_PAD_RATIO)
1628
+ chart_gap_px = max(
1629
+ _LABEL_CHART_GAP_PX_MIN, strain_label_font_size * _LABEL_CHART_GAP_RATIO
1630
+ )
1617
1631
  # Strip needs to fit the longest label plus `halo_px / 2` of halo
1618
- # extension on the leader-facing side, plus a small fixed pad.
1619
- label_pixel_width = label_pad_px + max_name_len * char_px + halo_px / 2
1632
+ # extension on the leader-facing side, a pad between the tree and the
1633
+ # text, and a smaller gap between the text and the chart.
1634
+ label_pixel_width = (
1635
+ label_pad_px + chart_gap_px + max_name_len * char_px + halo_px / 2
1636
+ )
1620
1637
  strip_pixel_width = label_pixel_width - shift_tree_loc
1621
1638
  if strip_pixel_width <= 0:
1622
1639
  raise ValueError(
@@ -1629,9 +1646,13 @@ def _build_tree_chart(
1629
1646
  per_pixel = branch_span / tree_size if tree_size else 0.0
1630
1647
  extra_branch_units = strip_pixel_width * per_pixel
1631
1648
  chart_edge_branch = branch_max + extra_branch_units
1632
- tips_df = tips_df.assign(x_label=chart_edge_branch)
1633
- leader_df = tips_df[tips_df["x"] < chart_edge_branch].assign(
1634
- x2=chart_edge_branch
1649
+ # Anchor the labels `chart_gap_px` *inside* the chart-facing edge rather
1650
+ # than on it, so the text does not run up against the chart's frame. The
1651
+ # leaders stop at the same place, where the text begins.
1652
+ label_anchor_branch = chart_edge_branch - chart_gap_px * per_pixel
1653
+ tips_df = tips_df.assign(x_label=label_anchor_branch)
1654
+ leader_df = tips_df[tips_df["x"] < label_anchor_branch].assign(
1655
+ x2=label_anchor_branch
1635
1656
  )
1636
1657
  extended_tree_size = tree_size + strip_pixel_width
1637
1658
  else:
@@ -1680,9 +1701,9 @@ def _build_tree_chart(
1680
1701
  # When connect_leader_to_label is on, the branch domain is extended
1681
1702
  # past `branch_max` to `chart_edge_branch` so the label strip has
1682
1703
  # data-units to occupy; tips at `branch_max` still sit at pixel
1683
- # `tree_size` on the panel. Each label's chart-facing edge is
1684
- # anchored at `chart_edge_branch` and aligned outward (right for
1685
- # tree on the left, left for tree on the right).
1704
+ # `tree_size` on the panel. Each label's chart-facing edge sits
1705
+ # `chart_gap_px` inside `chart_edge_branch`, aligned outward (right
1706
+ # for tree on the left, left for tree on the right).
1686
1707
  if tree_location == "left":
1687
1708
  branch_domain = [branch_min, chart_edge_branch]
1688
1709
  text_align = "right"
@@ -1783,7 +1804,8 @@ def _build_tree_chart(
1783
1804
  # strip at bottom, opposite the chart above).
1784
1805
  # tree_location="bottom" → root at bottom → branch_max at top (label
1785
1806
  # strip at top, opposite the chart below).
1786
- # Each label's chart-facing edge is anchored at `chart_edge_branch`.
1807
+ # Each label's chart-facing edge sits `chart_gap_px` inside
1808
+ # `chart_edge_branch`.
1787
1809
  # The text mark is rotated 270° (reads bottom-to-top), which maps
1788
1810
  # pre-rotation `align="right"` to a top anchor (text extends down)
1789
1811
  # and `align="left"` to a bottom anchor (text extends up). For tree
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: tree-annotated-plot
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Annotate the axis of an Altair / Vega-Lite plot with a phylogenetic tree.
5
5
  Project-URL: Documentation, https://jbloomlab.github.io/tree-annotated-plot/
6
6
  Project-URL: Homepage, https://github.com/jbloomlab/tree-annotated-plot
@@ -43,7 +43,7 @@ Requires-Dist: vl-convert-python>=1.9
43
43
  Provides-Extra: dev
44
44
  Requires-Dist: black>=24; extra == 'dev'
45
45
  Requires-Dist: pytest>=8; extra == 'dev'
46
- Requires-Dist: ruff>=0.6; extra == 'dev'
46
+ Requires-Dist: ruff<0.17,>=0.16; extra == 'dev'
47
47
  Provides-Extra: docs
48
48
  Requires-Dist: mkdocs-click>=0.8; extra == 'docs'
49
49
  Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
@@ -0,0 +1,11 @@
1
+ tree_annotated_plot/__init__.py,sha256=R39gjVnl3K2VjLba6zwtwPGADFmtNOE2UYNk1y9_z6k,351
2
+ tree_annotated_plot/_color.py,sha256=nOZp-KIPy6s-6gBkzW36oHrwDk_SmvVNsMiIJ-dY1Io,26519
3
+ tree_annotated_plot/_config.py,sha256=0ib600JaMpMz4ZjFYtvFu3nhUhliJRexTJcFpDKVOD0,13171
4
+ tree_annotated_plot/_plot.py,sha256=pqhR9PgQUML2-taP_k1FEidpIdBgJ6tulMEpFJbD8WI,77969
5
+ tree_annotated_plot/_tree.py,sha256=Bj-ABt8aooSwV7wAVTbwFGMmCnTPGOKweUWAdfb7aEI,13563
6
+ tree_annotated_plot/cli.py,sha256=OG6Qmzk5BaHUNrfOopX20FbzBN2ahr_SBxHpaCk6Z5c,9848
7
+ tree_annotated_plot-0.4.0.dist-info/METADATA,sha256=JQNY2-mmZIQA7TjABRX_YkvoZXxqix-Ixs5n23EQ1FY,11462
8
+ tree_annotated_plot-0.4.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
9
+ tree_annotated_plot-0.4.0.dist-info/entry_points.txt,sha256=K7edTocRbwmtM2OeZmCy3DLjmY3MR-q2EJmQQq4rj0E,69
10
+ tree_annotated_plot-0.4.0.dist-info/licenses/LICENSE,sha256=UIp30ue1HZit7tMmSK0SS3swJBqOcLLnLJn5LY5YdNE,1036
11
+ tree_annotated_plot-0.4.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.29.0
2
+ Generator: hatchling 1.32.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,11 +0,0 @@
1
- tree_annotated_plot/__init__.py,sha256=R39gjVnl3K2VjLba6zwtwPGADFmtNOE2UYNk1y9_z6k,351
2
- tree_annotated_plot/_color.py,sha256=nOZp-KIPy6s-6gBkzW36oHrwDk_SmvVNsMiIJ-dY1Io,26519
3
- tree_annotated_plot/_config.py,sha256=Xs9XfuPAaIhGuffYbsDOfcJW_f4etpgPW4ombHNuIAg,12630
4
- tree_annotated_plot/_plot.py,sha256=a-sxcZ83Sp-bc0swwisOlvhV7L6bhJQWeh5kppANMX0,76901
5
- tree_annotated_plot/_tree.py,sha256=Bj-ABt8aooSwV7wAVTbwFGMmCnTPGOKweUWAdfb7aEI,13563
6
- tree_annotated_plot/cli.py,sha256=OG6Qmzk5BaHUNrfOopX20FbzBN2ahr_SBxHpaCk6Z5c,9848
7
- tree_annotated_plot-0.3.0.dist-info/METADATA,sha256=oQGwnxJxdq3g_fx_gfn6mjsskj0RHpBcMw2pGcJNZPo,11455
8
- tree_annotated_plot-0.3.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
9
- tree_annotated_plot-0.3.0.dist-info/entry_points.txt,sha256=K7edTocRbwmtM2OeZmCy3DLjmY3MR-q2EJmQQq4rj0E,69
10
- tree_annotated_plot-0.3.0.dist-info/licenses/LICENSE,sha256=UIp30ue1HZit7tMmSK0SS3swJBqOcLLnLJn5LY5YdNE,1036
11
- tree_annotated_plot-0.3.0.dist-info/RECORD,,