eaton-lab / toytree

A minimalist tree plotting library using toyplot graphs
http://eaton-lab.org/toytree
BSD 3-Clause "New" or "Revised" License
164 stars 28 forks source link

Styling individual tip labels #64

Open rotifyld opened 1 year ago

rotifyld commented 1 year ago

I found out about the package only yesterday, and I already love the project and its simplicity. The only thing I miss right now is that all the tip labels has to be styled identically (apart from the color property). I'm interested in contributing towards the codebase with this feature if you think it wouldn't break the minimalism/simplicity of the overall project. Also, any guidance on that would be appreciated.

And thank you for all the work you put into this project!

eaton-lab commented 1 year ago

Hi @rotifyld,

Thanks. I am very happy for new contributors.

As you may know, the toytree draw arguments for tip_label_colors and tip_label_angles currently supports either a single or multiple values. But this is not supported for the styles applied using the tip_labels_style dict, which apply to all tips the same. Here is an example for color:

image

I agree it would be nice to be able to style the tip labels in complex ways more easily.

For now, the simplest solution is to draw each text element (e.g., tip) separately by using toyplot in combination with toytree. For example, to draw tip labels of different font sizes, stroke colors, etc., you can do something like the following:

image

If we did want to support this type of thing within the .draw() function of toytree we could simply implement something like this under the hood, however, it becomes a bit more complicated with things like circular or unrooted trees. Moreover, I think the main drawback is that it creates a much more bloated SVG code, since each style element will be repeated ntips times in the code, rather than just once. That could be simplified after the fact by finding which style elements are shared and condensing the code (some of this is already done in the latest toytree branch) but its not a huge priority to support additional styles yet, given that the hack above can be used for now.

For the future (toytree version 3 in development branch 'toy3') I have an idea for how to implement this using a submodule toytree.annotate with functions for annotating trees after they are drawn. This would be the obvious place to create a function that automates the process similar to above.

I'm still deciding on the best syntax to use for this new module. It will likely be something like the following:

# create a tree drawing on a set of axes
canvas, axes, tree_mark = tree.draw()

# add additional marks to the axes taking the tree mark as an input
# b/c it contains the coordinates of the current tree drawing given its
# orientation and other drawing style arguments.
tip_marks = toytree.annotate.draw_tip_labels(axes, mark, ...)