Skip to content

Instantly share code, notes, and snippets.

@jhcepas
Created June 10, 2016 12:01
Show Gist options
  • Save jhcepas/2a37d1aa08cc2c3ffe3a4746e11d8a31 to your computer and use it in GitHub Desktop.
Save jhcepas/2a37d1aa08cc2c3ffe3a4746e11d8a31 to your computer and use it in GitHub Desktop.
# pp = painter
# node_links = a list of node links
# a = node a
# b = node b
# a_mode, b_mode: mode 0= node to node. mode 1: if node is internal cover also the descendants
# bg = background color
# lw = linewidth
# opa = opacity
# text, fsize, fcolor, ftype = defines text following the curve
# node_zoom_positions is a dictionary that does not exist in treeview, but the used values can be inferred from ETE's treeview scene:
# _rot = node rotation (angle)
# _rad = radius for the node positionb
# (the two values above are polar positions that could converted to cartesian or viceversa)
# _astart = angle start
# _aend = angle end
# (the angle covered by a given node, internal or leaf)
def draw_circ_node_links(pp, node_links, cx, cy, node_zoom_positions):
link_paths = []
#max_rad = max([reg[_rad] for reg in node_zoom_positions])
for a, b, a_mode, b_mode, bg, lw, opa, text, fsize, fcolor, ftype in node_links:
if node_zoom_positions[a._id][_rot] > node_zoom_positions[b._id][_rot]:
a, b = b, a
if b in set(a.get_ancestors()): ############ CACHE THIS!
b_mode = 0
elif a in set(b.get_ancestors()):
a_mode = 0
if not a.is_leaf() and a_mode == 1:
a_max_rad = 0
a_min_rot = 360
a_max_rot = 0
for lf in a.iter_leaves():
a_max_rad = max(a_max_rad, node_zoom_positions[lf._id][_rad])
a_min_rot = min(a_min_rot, node_zoom_positions[lf._id][_astart])
a_max_rot = max(a_max_rot, node_zoom_positions[lf._id][_aend])
a1x, a1y = get_absolute_coords(a_max_rad, a_min_rot, cx, cy)
a2x, a2y = get_absolute_coords(a_max_rad, a_max_rot, cx, cy)
acx, acy = get_absolute_coords(a_max_rad, a_min_rot +(a_max_rot-a_min_rot)/2, cx, cy)
else:
a_pos = node_zoom_positions[a._id]
a_max_rad = a_pos[_rad]
a_min_rot = a_max_rot = a_pos[_rot]
a1x, a1y = get_absolute_coords(a_pos[_rad], a_pos[_rot], cx, cy)
a2x, a2y = a1x, a1y
acx, acy = a1x, a1y
if not b.is_leaf() and b_mode == 1:
b_max_rad = 0
b_min_rot = 360
b_max_rot = 0
for lf in b.iter_leaves():
b_max_rad = max(b_max_rad, node_zoom_positions[lf._id][_rad])
b_min_rot = min(b_min_rot, node_zoom_positions[lf._id][_astart])
b_max_rot = max(b_max_rot, node_zoom_positions[lf._id][_aend])
b1x, b1y = get_absolute_coords(b_max_rad, b_min_rot, cx, cy)
b2x, b2y = get_absolute_coords(b_max_rad, b_max_rot, cx, cy)
bcx, bcy = get_absolute_coords(b_max_rad, b_min_rot + (b_max_rot-b_min_rot)/2, cx, cy)
else:
b_pos = node_zoom_positions[b._id]
b_max_rad = b_pos[_rad]
b_min_rot = b_max_rot = b_pos[_rot]
b1x, b1y = get_absolute_coords(b_pos[_rad], b_pos[_rot], cx, cy)
b2x, b2y = b1x, b1y
bcx, bcy = b1x, b1y
path = QPainterPath()
path.moveTo(a2x, a2y)
path.quadTo(cx, cy, b1x, b1y)
pp.setBrush(get_qbrush(None))
pp.setPen(get_qpen(bg, lw, 0, Qt.FlatCap))
if b1x != b2x or b1y != b2y:
path.arcTo(cx - b_max_rad, cy - b_max_rad,
b_max_rad*2, b_max_rad*2,
-b_min_rot, -(b_max_rot-b_min_rot))
path.quadTo(cx, cy, a1x, a1y)
pp.setPen(get_qpen(bg, 0, 0, Qt.FlatCap))
pp.setBrush(get_qbrush(bg))
else:
path.quadTo(cx, cy, a1x, a1y)
if a1x != a2x or a1y != a2y:
path.arcTo(cx - a_max_rad, cy - a_max_rad,
a_max_rad*2, a_max_rad*2,
-a_min_rot, -(a_max_rot-a_min_rot))
pp.setPen(get_qpen(bg, 0, 0, Qt.FlatCap))
pp.setBrush(get_qbrush(bg))
pp.setOpacity(opa)
pp.drawPath(path)
if bcx < acx:
acx, acy, bcx, bcy = bcx, bcy, acx, acy
txtpath = QPainterPath()
txtpath.moveTo(acx, acy)
txtpath.quadTo(cx, cy, bcx, bcy)
draw_text_in_path(pp, "Esto es una prueba muy muy larga para ver que pasa con las curvas",
txtpath, ftype, fcolor, fsize)
#draw_rings(pp, cx, cy, node_zoom_positions, node_links)
def draw_text_in_path(pp, text, follow_path, ftype, fcolor, fsize):
length = follow_path.length()
pp.setPen(get_qpen(fcolor))
font = get_qfont(ftype, fsize, False)
pp.setFont(font)
fm = QFontMetrics(font)
text_width = fm.width(text)
# approx center text in path
current_length = (length/2) - (text_width/2)
for letter in text:
perc = follow_path.percentAtLength(current_length)
point = QPointF(follow_path.pointAtPercent(perc))
angle = follow_path.angleAtPercent(perc)
current_length += fm.width(letter)
pp.save()
pp.translate(point)
pp.rotate(-angle)
pp.drawText(QPoint(0, 0), letter)
pp.restore()
@jhcepas
Copy link
Author

jhcepas commented Jun 10, 2016

does not work alone, but this is the relevant code used under a different project to draw HGT events using ETE circular trees.
Examples and discussion here: etetoolkit/ete#161

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment