trackplot 0.6.4__tar.gz → 0.6.5__tar.gz
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.
- {trackplot-0.6.4/trackplot.egg-info → trackplot-0.6.5}/PKG-INFO +1 -1
- {trackplot-0.6.4 → trackplot-0.6.5}/pyproject.toml +4 -1
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/plot/coord.py +29 -15
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/plot/core.py +39 -15
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/plot/limits.py +15 -15
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/plot/render.py +105 -20
- {trackplot-0.6.4 → trackplot-0.6.5/trackplot.egg-info}/PKG-INFO +1 -1
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot.egg-info/SOURCES.txt +1 -0
- trackplot-0.6.5/trackplot.egg-info/entry_points.txt +2 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/LICENSE +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/README.md +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/setup.cfg +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/__init__.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/anno/AxLabel.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/anno/__init__.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/anno/theme.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/CoordinateMap.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/GenomicLoci.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/Junction.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/Protein.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/ReadDepth.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/Readder.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/Stroke.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/Transcript.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/__init__.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/base/pyUniprot.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/cli.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/conf/DomainSetting.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/conf/__init__.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/conf/config.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/conf/drawing.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/conf/ui.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/ATAC.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/Annotation.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/Bam.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/BedGraph.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/Bigwig.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/Depth.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/Fasta.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/File.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/HiCMatrixTrack.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/Junction.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/Motif.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/ReadSegments.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/file/__init__.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/plot/__init__.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/plot/info.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/plot/utils.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/plot_func.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot/server.py +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot.egg-info/dependency_links.txt +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot.egg-info/requires.txt +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/trackplot.egg-info/top_level.txt +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/Home-B1EPE6Lq.js +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/Home-DuHqFHEh.css +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/Plot-CkylK0fA.css +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/Plot-jZAsK7Tq.js +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/_plugin-vue_export-helper-DLf8tVwz.css +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/_plugin-vue_export-helper-IgiCOkTJ.js +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/css-BDwM7dEv.css +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/css-CKLNJEB6.js +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/index-De-rfTdP.js +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/assets/index-Dlq_kUQH.css +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/index.html +0 -0
- {trackplot-0.6.4 → trackplot-0.6.5}/ui/vite.svg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trackplot
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: The trackplot is a tool for visualizing various next-generation sequencing (NGS) data, including DNA-seq, RNA-seq, single-cell RNA-seq and full-length sequencing datasets. https://sashimi.readthedocs.io/
|
|
5
5
|
Author-email: ygidtu <ygidtu@gmail.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "trackplot"
|
|
3
|
-
version = "0.6.
|
|
3
|
+
version = "0.6.5"
|
|
4
4
|
description = "The trackplot is a tool for visualizing various next-generation sequencing (NGS) data, including DNA-seq, RNA-seq, single-cell RNA-seq and full-length sequencing datasets. https://sashimi.readthedocs.io/"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "ygidtu", email = "ygidtu@gmail.com" }
|
|
@@ -29,6 +29,9 @@ dependencies = [
|
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
[project.scripts]
|
|
33
|
+
trackplot = "trackplot.cli:main" # 假设入口在 trackplot/cli.py 的 main 函数
|
|
34
|
+
|
|
32
35
|
[build-system]
|
|
33
36
|
requires = ["setuptools>=65.0", "wheel"]
|
|
34
37
|
build-backend = "setuptools.build_meta"
|
|
@@ -40,31 +40,45 @@ def init_graph_coords(
|
|
|
40
40
|
|
|
41
41
|
if exons:
|
|
42
42
|
if intron_scale <= 1:
|
|
43
|
+
# 填充首个外显子前的内含子区域
|
|
43
44
|
for i in range(0, exons[0][0] - region.start):
|
|
44
45
|
graph_coords[i] = (i - 0) * intron_scale
|
|
45
46
|
exons = __merge_exons__(exons)
|
|
46
47
|
for i in range(0, len(exons)):
|
|
47
48
|
exon = exons[i]
|
|
49
|
+
|
|
50
|
+
# 外显子间内含子(从上一个外显子结束的下一碱基开始,避免重叠)
|
|
48
51
|
if i > 0:
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
intron_start = exons[i - 1][1] + 1
|
|
53
|
+
intron_end = exons[i][0]
|
|
54
|
+
for j in range(intron_start, intron_end):
|
|
51
55
|
if j >= region.start:
|
|
52
56
|
graph_coords[j - region.start] = (
|
|
53
|
-
graph_coords[
|
|
54
|
-
+ (j -
|
|
57
|
+
graph_coords[intron_start - region.start - 1]
|
|
58
|
+
+ (j - intron_start + 1) * intron_scale
|
|
55
59
|
)
|
|
60
|
+
|
|
61
|
+
# 填充外显子区域
|
|
56
62
|
for j in range(exon[0], exon[1] + 1):
|
|
57
63
|
if j >= region.start:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
base_idx = exon[0] - region.start - 1
|
|
65
|
+
if base_idx < 0:
|
|
66
|
+
# 外显子起始位置在区域起点之前或等于起点,从 0 开始
|
|
67
|
+
graph_coords[j - region.start] = (j - region.start) * exon_scale
|
|
68
|
+
else:
|
|
69
|
+
graph_coords[j - region.start] = (
|
|
70
|
+
graph_coords[base_idx]
|
|
71
|
+
+ (j - exon[0] + 1) * exon_scale
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# 填充最后一个外显子之后的内含子(从下一碱基开始,到 region.end 结束)
|
|
75
|
+
intron_start = exons[-1][-1] + 1
|
|
76
|
+
intron_end = region.end + 1 # +1 确保包含 region.end
|
|
77
|
+
for i in range(intron_start, intron_end):
|
|
64
78
|
if i >= region.start:
|
|
65
79
|
graph_coords[i - region.start] = (
|
|
66
|
-
graph_coords[
|
|
67
|
-
+ (i -
|
|
80
|
+
graph_coords[intron_start - region.start - 1]
|
|
81
|
+
+ (i - intron_start + 1) * intron_scale
|
|
68
82
|
)
|
|
69
83
|
else:
|
|
70
84
|
exons = __merge_exons__(exons)
|
|
@@ -85,14 +99,14 @@ def init_graph_coords(
|
|
|
85
99
|
for i in range(exons[0][0]):
|
|
86
100
|
steps[i] = step
|
|
87
101
|
for e in range(1, len(exons)):
|
|
88
|
-
interval = exons[e][0] - exons[e - 1][1] -
|
|
102
|
+
interval = exons[e][0] - exons[e - 1][1] - 1
|
|
89
103
|
step = intron_scale / interval
|
|
90
|
-
for i in range(exons[e - 1][1], exons[e][0]):
|
|
104
|
+
for i in range(exons[e - 1][1] + 1, exons[e][0]):
|
|
91
105
|
steps[i] = step
|
|
92
106
|
|
|
93
107
|
if last_interval := len(region) - exons[-1][1] - 1:
|
|
94
108
|
step = intron_scale / last_interval
|
|
95
|
-
for i in range(exons[1][1] + 1, len(region)):
|
|
109
|
+
for i in range(exons[-1][1] + 1, len(region)):
|
|
96
110
|
steps[i] = step
|
|
97
111
|
graph_coords = list(map(int, itertools.accumulate(steps)))
|
|
98
112
|
else:
|
|
@@ -866,20 +866,25 @@ class Plot(object):
|
|
|
866
866
|
return default_y
|
|
867
867
|
|
|
868
868
|
def _precompute_plot_y_limits(
|
|
869
|
-
self, p, max_used_y_dict, min_used_y_dict, same_y_by_groups, default_y, **kwargs
|
|
869
|
+
self, p, max_used_y_dict, min_used_y_dict, base_max_dict, same_y_by_groups, default_y, **kwargs
|
|
870
870
|
):
|
|
871
|
-
"""Compute and store y-limits for all objects in a plot.
|
|
871
|
+
"""Compute and store y-limits for all objects in a plot.
|
|
872
|
+
|
|
873
|
+
Also collects base_max (raw data maximum) per object path for use as
|
|
874
|
+
a global arc-height reference in --same-y mode.
|
|
875
|
+
"""
|
|
872
876
|
if p.type not in ("density", "site-plot", "line"):
|
|
873
877
|
return
|
|
874
878
|
|
|
875
879
|
for obj in p.obj:
|
|
876
|
-
_max, _min = precompute_y_limits(
|
|
880
|
+
_max, _min, _base = precompute_y_limits(
|
|
877
881
|
obj,
|
|
878
882
|
data=obj.data,
|
|
879
883
|
region=self.region,
|
|
880
884
|
graph_coords=self.graph_coords,
|
|
881
885
|
**kwargs,
|
|
882
886
|
)
|
|
887
|
+
base_max_dict[obj.path] = max(_base, base_max_dict.get(obj.path, 0))
|
|
883
888
|
|
|
884
889
|
if obj.label in same_y_by_groups:
|
|
885
890
|
key = same_y_by_groups[obj.label]
|
|
@@ -890,10 +895,11 @@ class Plot(object):
|
|
|
890
895
|
if obj.data.minus is None
|
|
891
896
|
else min_used_y_dict.get(obj.path, 0),
|
|
892
897
|
)
|
|
898
|
+
base_max_dict[key] = max(_base, base_max_dict.get(key, 0))
|
|
893
899
|
|
|
894
900
|
if isinstance(obj.data, dict):
|
|
895
901
|
for key, readDepth in obj.data.items():
|
|
896
|
-
_max, _min = precompute_y_limits(
|
|
902
|
+
_max, _min, _base = precompute_y_limits(
|
|
897
903
|
obj,
|
|
898
904
|
data=readDepth,
|
|
899
905
|
graph_coords=self.graph_coords,
|
|
@@ -907,6 +913,7 @@ class Plot(object):
|
|
|
907
913
|
if readDepth.minus is None
|
|
908
914
|
else min_used_y_dict.get(obj.path, 0),
|
|
909
915
|
)
|
|
916
|
+
base_max_dict[key] = max(_base, base_max_dict.get(key, 0))
|
|
910
917
|
|
|
911
918
|
continue
|
|
912
919
|
|
|
@@ -928,21 +935,29 @@ class Plot(object):
|
|
|
928
935
|
min_used_y_dict[key] = min_used_y_dict[obj.path]
|
|
929
936
|
|
|
930
937
|
def _resolve_plot_y_limits(
|
|
931
|
-
self, p, max_used_y_val, min_used_y_val, same_y_by_groups, default_y, **kwargs
|
|
938
|
+
self, p, max_used_y_val, min_used_y_val, base_max_val, same_y_by_groups, default_y, **kwargs
|
|
932
939
|
):
|
|
933
|
-
"""Determine y-limits for a single plot based on same-y / default-y settings.
|
|
940
|
+
"""Determine y-limits for a single plot based on same-y / default-y settings.
|
|
941
|
+
|
|
942
|
+
Returns (max_y, min_y, global_base_max).
|
|
943
|
+
global_base_max is the max of all base_max values (for --same-y mode),
|
|
944
|
+
used as the arc height reference so all panels share consistent arc heights.
|
|
945
|
+
"""
|
|
934
946
|
same_y_sc = kwargs.get("same_y_sc")
|
|
935
947
|
same_y = kwargs.get("same_y")
|
|
936
948
|
|
|
937
949
|
if same_y_sc and p.obj[0].is_single_cell:
|
|
938
|
-
|
|
950
|
+
_bm = base_max_val.get(p.obj[0].path) if base_max_val else None
|
|
951
|
+
return max_used_y_val.get(p.obj[0].path), min_used_y_val.get(p.obj[0].path), _bm
|
|
939
952
|
|
|
940
953
|
if same_y_by_groups and p.obj[0].label in same_y_by_groups:
|
|
941
954
|
key = same_y_by_groups[p.obj[0].label]
|
|
942
|
-
|
|
955
|
+
_bm = base_max_val.get(key) if base_max_val else None
|
|
956
|
+
return max_used_y_val.get(key), min_used_y_val.get(key), _bm
|
|
943
957
|
|
|
944
958
|
if same_y and max_used_y_val:
|
|
945
|
-
|
|
959
|
+
_bm = max(base_max_val.values()) if base_max_val else None
|
|
960
|
+
return max(max_used_y_val.values()), min(min_used_y_val.values()), _bm
|
|
946
961
|
|
|
947
962
|
if default_y and p.type in ("density", "site-plot", "line"):
|
|
948
963
|
for obj in p.obj:
|
|
@@ -956,8 +971,8 @@ class Plot(object):
|
|
|
956
971
|
if same_y_by_groups and obj.label in same_y_by_groups:
|
|
957
972
|
max_y = max_used_y_val.get(obj.path, max_y)
|
|
958
973
|
min_y = min_used_y_val.get(obj.path, min_y)
|
|
959
|
-
return max_y, min_y
|
|
960
|
-
return None, None
|
|
974
|
+
return max_y, min_y, None
|
|
975
|
+
return None, None, None
|
|
961
976
|
|
|
962
977
|
# ------------------------------------------------------------------
|
|
963
978
|
# Main plot method
|
|
@@ -1015,7 +1030,7 @@ class Plot(object):
|
|
|
1015
1030
|
)
|
|
1016
1031
|
|
|
1017
1032
|
# ====== Phase 4: Precompute global y-limits ======
|
|
1018
|
-
max_used_y_val, min_used_y_val = {}, {}
|
|
1033
|
+
max_used_y_val, min_used_y_val, base_max_val = {}, {}, {}
|
|
1019
1034
|
same_y_by_groups = {}
|
|
1020
1035
|
default_y = self._load_default_y(kwargs.get("y_limit"))
|
|
1021
1036
|
|
|
@@ -1039,6 +1054,7 @@ class Plot(object):
|
|
|
1039
1054
|
p,
|
|
1040
1055
|
max_used_y_val,
|
|
1041
1056
|
min_used_y_val,
|
|
1057
|
+
base_max_val,
|
|
1042
1058
|
same_y_by_groups,
|
|
1043
1059
|
default_y,
|
|
1044
1060
|
**kwargs,
|
|
@@ -1056,22 +1072,27 @@ class Plot(object):
|
|
|
1056
1072
|
if curr_idx == 0 and not no_title:
|
|
1057
1073
|
ax_var.set_title(title or str(self.region), loc="left")
|
|
1058
1074
|
|
|
1059
|
-
max_y_val_, min_y_val_ = self._resolve_plot_y_limits(
|
|
1075
|
+
max_y_val_, min_y_val_, base_max_ = self._resolve_plot_y_limits(
|
|
1060
1076
|
p,
|
|
1061
1077
|
max_used_y_val,
|
|
1062
1078
|
min_used_y_val,
|
|
1079
|
+
base_max_val,
|
|
1063
1080
|
same_y_by_groups,
|
|
1064
1081
|
default_y,
|
|
1065
1082
|
same_y_sc=kwargs.get("same_y_sc"),
|
|
1066
1083
|
same_y=kwargs.get("same_y"),
|
|
1067
1084
|
)
|
|
1068
|
-
|
|
1085
|
+
|
|
1069
1086
|
if max_y_val_ is not None:
|
|
1070
1087
|
max_y_val_ *= 1.1
|
|
1071
|
-
|
|
1088
|
+
|
|
1072
1089
|
if min_y_val_ is not None:
|
|
1073
1090
|
min_y_val_ *= 1.1
|
|
1074
1091
|
|
|
1092
|
+
# For --same-y mode: use global base_max as arc height reference
|
|
1093
|
+
# so all panels draw junctions at a consistent scale.
|
|
1094
|
+
global_arc_ref = base_max_
|
|
1095
|
+
|
|
1075
1096
|
logger.info(
|
|
1076
1097
|
f"plotting {p.type} at idx: {curr_idx} with height_ratio: {height_ratio[curr_idx]}"
|
|
1077
1098
|
)
|
|
@@ -1094,6 +1115,7 @@ class Plot(object):
|
|
|
1094
1115
|
raster=raster,
|
|
1095
1116
|
fill_step=fill_step,
|
|
1096
1117
|
junctions_on_top=junctions_on_top,
|
|
1118
|
+
global_arc_ref=global_arc_ref,
|
|
1097
1119
|
**temp_params,
|
|
1098
1120
|
)
|
|
1099
1121
|
curr_idx += 1
|
|
@@ -1110,6 +1132,7 @@ class Plot(object):
|
|
|
1110
1132
|
raster=raster,
|
|
1111
1133
|
fill_step=fill_step,
|
|
1112
1134
|
junctions_on_top=junctions_on_top,
|
|
1135
|
+
global_arc_ref=global_arc_ref,
|
|
1113
1136
|
**self.params.get(p, {}),
|
|
1114
1137
|
)
|
|
1115
1138
|
elif p.type == "hic":
|
|
@@ -1131,6 +1154,7 @@ class Plot(object):
|
|
|
1131
1154
|
distance_between_label_axis=distance_between_label_axis,
|
|
1132
1155
|
raster=raster,
|
|
1133
1156
|
junctions_on_top=junctions_on_top,
|
|
1157
|
+
global_arc_ref=global_arc_ref,
|
|
1134
1158
|
**self.params.get(p, {}),
|
|
1135
1159
|
)
|
|
1136
1160
|
curr_idx += 1
|
|
@@ -27,14 +27,16 @@ def _compute_y_limits(
|
|
|
27
27
|
fill_step: str = "post",
|
|
28
28
|
show_mean_jxn_number: bool = False,
|
|
29
29
|
junctions_on_top: bool = False,
|
|
30
|
-
) -> Tuple[float, float]:
|
|
30
|
+
) -> Tuple[float, float, float]:
|
|
31
31
|
"""
|
|
32
32
|
Compute y-axis limits from ReadDepth data, including junction arc extents.
|
|
33
33
|
|
|
34
34
|
This is the shared core used by both precompute_y_limits() and plot_density()
|
|
35
35
|
to avoid code duplication.
|
|
36
36
|
|
|
37
|
-
Returns (max_used_y_val, min_used_y_val).
|
|
37
|
+
Returns (max_used_y_val, min_used_y_val, base_max).
|
|
38
|
+
base_max is the raw data maximum before junction arc expansion, used as
|
|
39
|
+
arc height reference for --same-y cross-panel consistency.
|
|
38
40
|
"""
|
|
39
41
|
# --- Determine data-driven baseline values for arc height ---
|
|
40
42
|
# These baselines capture the "true" data range before junction expansion.
|
|
@@ -70,19 +72,15 @@ def _compute_y_limits(
|
|
|
70
72
|
if min_used_y_val is None:
|
|
71
73
|
min_used_y_val = base_min
|
|
72
74
|
|
|
73
|
-
# --- Arc heights:
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
75
|
+
# --- Arc heights: always based on the data baseline ---
|
|
76
|
+
# Previously this used max(base_max, max_used_y_val) which caused
|
|
77
|
+
# arc height inflation when max_used_y_val was already expanded by
|
|
78
|
+
# the precompute step. The inflated arc peak then exceeded the
|
|
79
|
+
# y-axis limit and overflowed the panel.
|
|
80
|
+
# Use base_max / base_min exclusively — the limit expansion logic
|
|
81
|
+
# below still ensures the axis range grows to accommodate the arcs.
|
|
77
82
|
_arc_ref_max = base_max
|
|
78
|
-
if max_used_y_val is not None:
|
|
79
|
-
_arc_ref_max = max(base_max, max_used_y_val)
|
|
80
|
-
|
|
81
83
|
_arc_ref_min = base_min
|
|
82
|
-
if min_used_y_val is not None:
|
|
83
|
-
_arc_ref_min = min(
|
|
84
|
-
base_min, min_used_y_val
|
|
85
|
-
) # both negative → pick the more negative
|
|
86
84
|
|
|
87
85
|
top_arc_height = abs(3 * _arc_ref_max / 4)
|
|
88
86
|
bot_arc_height = abs(3 * _arc_ref_min / 4) if _arc_ref_min != 0 else top_arc_height
|
|
@@ -174,7 +172,7 @@ def _compute_y_limits(
|
|
|
174
172
|
max_used_y_val *= 1.1
|
|
175
173
|
min_used_y_val *= 1.1
|
|
176
174
|
|
|
177
|
-
return max_used_y_val, min_used_y_val
|
|
175
|
+
return max_used_y_val, min_used_y_val, base_max
|
|
178
176
|
|
|
179
177
|
|
|
180
178
|
def precompute_y_limits(
|
|
@@ -201,7 +199,7 @@ def precompute_y_limits(
|
|
|
201
199
|
if data is None:
|
|
202
200
|
data = obj.data
|
|
203
201
|
|
|
204
|
-
|
|
202
|
+
_max, _min, _base = _compute_y_limits(
|
|
205
203
|
data=data,
|
|
206
204
|
region=region,
|
|
207
205
|
graph_coords=graph_coords,
|
|
@@ -212,3 +210,5 @@ def precompute_y_limits(
|
|
|
212
210
|
show_mean_jxn_number=False,
|
|
213
211
|
junctions_on_top=kwargs.get("junctions_on_top", False),
|
|
214
212
|
)
|
|
213
|
+
|
|
214
|
+
return _max, _min, _base
|
|
@@ -406,6 +406,87 @@ def _plot_local_domain(
|
|
|
406
406
|
# ============================================================================
|
|
407
407
|
|
|
408
408
|
|
|
409
|
+
def _auto_adjust_horizontal(
|
|
410
|
+
text_objs: List,
|
|
411
|
+
ax,
|
|
412
|
+
padding_px: float = 4.0,
|
|
413
|
+
max_iter: int = 20,
|
|
414
|
+
):
|
|
415
|
+
"""
|
|
416
|
+
水平碰撞检测与自动调整 —— 替代 adjust_text 的 2D 随意移动。
|
|
417
|
+
将所有文本沿弧线水平排列,通过贪婪算法确保相邻标签互不重叠。
|
|
418
|
+
|
|
419
|
+
原理:
|
|
420
|
+
1. 获取每个文本在显示坐标中的包围盒,计算其数据坐标宽度。
|
|
421
|
+
2. 按 x 坐标排序,从左到右逐对检查。
|
|
422
|
+
3. 如果前一个的右边缘 + 间距 > 后一个的左边缘,则把后一个向右推。
|
|
423
|
+
4. 重复迭代直到没有标签需要移动为止。
|
|
424
|
+
|
|
425
|
+
:param text_objs: matplotlib.text.Text 对象列表
|
|
426
|
+
:param ax: matplotlib Axes
|
|
427
|
+
:param padding_px: 标签之间的最小间距(像素)
|
|
428
|
+
:param max_iter: 最大迭代次数
|
|
429
|
+
"""
|
|
430
|
+
if len(text_objs) < 2:
|
|
431
|
+
return
|
|
432
|
+
|
|
433
|
+
try:
|
|
434
|
+
fig = ax.figure
|
|
435
|
+
fig.canvas.draw()
|
|
436
|
+
except Exception:
|
|
437
|
+
return # 无法渲染时直接跳过调整
|
|
438
|
+
|
|
439
|
+
inv_trans = ax.transData.inverted()
|
|
440
|
+
|
|
441
|
+
# ---- 收集每个标签的数据坐标宽度 ----
|
|
442
|
+
items = []
|
|
443
|
+
for t in text_objs:
|
|
444
|
+
x_data, y_data = t.get_position()
|
|
445
|
+
bbox = t.get_window_extent()
|
|
446
|
+
|
|
447
|
+
# 像素 → 数据坐标
|
|
448
|
+
left_data = inv_trans.transform((bbox.x0, bbox.y0))[0]
|
|
449
|
+
right_data = inv_trans.transform((bbox.x1, bbox.y0))[0]
|
|
450
|
+
half_w = (right_data - left_data) / 2.0
|
|
451
|
+
|
|
452
|
+
# 间距(像素 → 数据坐标)
|
|
453
|
+
p0 = inv_trans.transform((0, 0))[0]
|
|
454
|
+
p1 = inv_trans.transform((padding_px, 0))[0]
|
|
455
|
+
pad = abs(p1 - p0)
|
|
456
|
+
|
|
457
|
+
items.append({
|
|
458
|
+
"obj": t,
|
|
459
|
+
"x": x_data,
|
|
460
|
+
"y": y_data,
|
|
461
|
+
"half": half_w,
|
|
462
|
+
"pad": pad,
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
# 按 x 排序
|
|
466
|
+
items.sort(key=lambda d: d["x"])
|
|
467
|
+
|
|
468
|
+
# ---- 贪婪水平调整 ----
|
|
469
|
+
for _ in range(max_iter):
|
|
470
|
+
any_adjusted = False
|
|
471
|
+
|
|
472
|
+
for i in range(len(items) - 1):
|
|
473
|
+
cur = items[i]
|
|
474
|
+
nxt = items[i + 1]
|
|
475
|
+
|
|
476
|
+
cur_right = cur["x"] + cur["half"]
|
|
477
|
+
nxt_left = nxt["x"] - nxt["half"]
|
|
478
|
+
min_gap = max(cur["pad"], nxt["pad"])
|
|
479
|
+
|
|
480
|
+
need = cur_right + min_gap - nxt_left
|
|
481
|
+
if need > 0:
|
|
482
|
+
nxt["x"] += need
|
|
483
|
+
nxt["obj"].set_position((nxt["x"], nxt["y"]))
|
|
484
|
+
any_adjusted = True
|
|
485
|
+
|
|
486
|
+
if not any_adjusted:
|
|
487
|
+
break
|
|
488
|
+
|
|
489
|
+
|
|
409
490
|
def plot_density(
|
|
410
491
|
ax,
|
|
411
492
|
obj: Optional[File] = None,
|
|
@@ -460,6 +541,11 @@ def plot_density(
|
|
|
460
541
|
# drawn junction arcs have exactly the same height that was used to expand
|
|
461
542
|
# the y-axis limits. Using the already-expanded max_used_y_val for arc
|
|
462
543
|
# height causes non-convergent growth and truncated junction arcs.
|
|
544
|
+
|
|
545
|
+
# Determine whether y-limits were externally provided (e.g. from --same-y).
|
|
546
|
+
# Must be computed here BEFORE the arc-height block below uses it.
|
|
547
|
+
fixed_max_used_y = max_used_y_val is not None
|
|
548
|
+
|
|
463
549
|
if isinstance(data, dict):
|
|
464
550
|
_base_max = max(max(v.plus) if v.plus is not None else 0 for v in data.values())
|
|
465
551
|
_minus_maxes = [
|
|
@@ -473,17 +559,20 @@ def plot_density(
|
|
|
473
559
|
if _base_max % 2 == 1:
|
|
474
560
|
_base_max += 1
|
|
475
561
|
|
|
476
|
-
# Arc height reference: use
|
|
477
|
-
#
|
|
478
|
-
#
|
|
479
|
-
#
|
|
480
|
-
#
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
)
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
562
|
+
# Arc height reference: use global_arc_ref (from --same-y) when available
|
|
563
|
+
# and when y-limits were externally provided. This ensures panels with
|
|
564
|
+
# small data ranges don't get tiny arcs flattened against the x-axis when
|
|
565
|
+
# the panel's y-range is scaled up to match a larger panel.
|
|
566
|
+
#
|
|
567
|
+
# NOTE: we only use global_arc_ref when fixed_max_used_y is True so that
|
|
568
|
+
# standalone panels (no --same-y) preserve their original behavior.
|
|
569
|
+
global_arc_ref = kwargs.get("global_arc_ref")
|
|
570
|
+
if fixed_max_used_y and global_arc_ref is not None:
|
|
571
|
+
_arc_ref_max = max(_base_max, global_arc_ref)
|
|
572
|
+
_arc_ref_min = max(abs(_base_min), global_arc_ref) if _base_min != 0 else _arc_ref_max
|
|
573
|
+
else:
|
|
574
|
+
_arc_ref_max = _base_max
|
|
575
|
+
_arc_ref_min = _base_min
|
|
487
576
|
|
|
488
577
|
_top_arc_height = abs(3 * _arc_ref_max / 4)
|
|
489
578
|
_bot_arc_height = (
|
|
@@ -491,11 +580,9 @@ def plot_density(
|
|
|
491
580
|
)
|
|
492
581
|
|
|
493
582
|
# Compute y limits using shared logic
|
|
494
|
-
fixed_max_used_y = max_used_y_val is not None
|
|
495
|
-
|
|
496
583
|
junctions_on_top = kwargs.get("junctions_on_top", False)
|
|
497
584
|
|
|
498
|
-
max_used_y_val, min_used_y_val = _compute_y_limits(
|
|
585
|
+
max_used_y_val, min_used_y_val, _base = _compute_y_limits(
|
|
499
586
|
data=data,
|
|
500
587
|
region=region,
|
|
501
588
|
graph_coords=graph_coords,
|
|
@@ -628,17 +715,15 @@ def plot_density(
|
|
|
628
715
|
va="center",
|
|
629
716
|
backgroundcolor="w",
|
|
630
717
|
)
|
|
631
|
-
t.set_bbox(dict(alpha=0))
|
|
718
|
+
t.set_bbox(dict(alpha=0, pad=0.5))
|
|
632
719
|
text_objs.append(t)
|
|
633
720
|
|
|
634
721
|
if show_junction_number and len(text_objs) > 1:
|
|
635
|
-
|
|
722
|
+
_auto_adjust_horizontal(
|
|
636
723
|
text_objs,
|
|
637
724
|
ax=ax,
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
arrowprops=None,
|
|
641
|
-
lim=100,
|
|
725
|
+
padding_px=4.0,
|
|
726
|
+
max_iter=20,
|
|
642
727
|
)
|
|
643
728
|
|
|
644
729
|
if obj and obj.title:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trackplot
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: The trackplot is a tool for visualizing various next-generation sequencing (NGS) data, including DNA-seq, RNA-seq, single-cell RNA-seq and full-length sequencing datasets. https://sashimi.readthedocs.io/
|
|
5
5
|
Author-email: ygidtu <ygidtu@gmail.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|