arklumpus / TreeViewer

Cross-platform software to draw phylogenetic trees
GNU Affero General Public License v3.0
187 stars 9 forks source link

How to align the highlight bins and the images automatically #32

Closed klcym closed 6 months ago

klcym commented 6 months ago

Dear Giorgio :

I want to align the highlight bins and the images and I dont want to change the tree into the cladogram style. What can I do?

image

Thanks for your help.

arklumpus commented 6 months ago

Hi! For the images, within the options for the Draw image module, you need to set the Anchor to Origin, and then use the same X value for the position of all images.

For the highlights, I'm afraid there is no easy way to do this at the moment; you would have to manually adjust the Right margin of each highlight. I'll figure something out for the next update!

Another alternative would be to use a custom script to draw the highlights - I can give you some example code to get started, if you're interested.

klcym commented 6 months ago

Thanks for your reply. I will try to use the custom script.😊

arklumpus commented 6 months ago

OK, here is an example that produces the following tree:

customHighlights

And here is the tree file: customHighlights.zip

Essentially, the highlight colour is stored as an attribute on the LCA of each highlighted group; the script searches for nodes with this attribute and draws a rectangle around them.

Here is the source code (you can also access it from the tree file, by opening the options of the Custom script module.

Source code ```CSharp using PhyloTree; using System.Collections.Generic; using VectSharp; using TreeViewer; using System.Linq; namespace a6d3baabb494a4099b6ff50915d4ed748 { //Do not change class name public static class CustomCode { //Do not change method signature public static Point[] PerformPlotAction(TreeNode tree, Dictionary coordinates, Graphics graphics, InstanceStateData stateData) { // Name of the attribute where the highlight colour is stored. string attribute = "HighlightColour"; // Margins for the highlight. double marginTop = 6; double marginLeft = 10; double marginBottom = 6; double marginRight = 25; // Get the X coordinate that is farthest from the root. double rightmostX = coordinates.Values.Select(point => point.X).Max(); // Traverse the tree foreach (TreeNode node in tree.GetChildrenRecursiveLazy()) { // Check if the node has the attribute if (node.Attributes.TryGetValue(attribute, out object attributeVal) && attributeVal is string attributeString) { // Parse the colour. Colour highlightCol = Colour.FromCSSString(attributeString).Value; // The left side of the highlight corresponds to the coordinates of the node (minus the margin). double leftX = coordinates[node.Id].X - marginLeft; // The right side of the highlight is fixed. double rightX = rightmostX + marginRight; // The top of the highlight will be at the minimum Y coordinate of the node's children. double topY = node.GetChildrenRecursiveLazy().Select(childNode => coordinates[childNode.Id].Y).Min() - marginTop; // The bottom of the highlight will be at the maximum Y coordinate of the node's children. double bottomY = node.GetChildrenRecursiveLazy().Select(childNode => coordinates[childNode.Id].Y).Max() + marginBottom; // Create the linear gradient brush. LinearGradientBrush brush = new LinearGradientBrush( new Point(leftX, (topY + bottomY) * 0.5), // Start point new Point(rightX, (topY + bottomY) * 0.5), // End point new VectSharp.GradientStop(highlightCol.WithAlpha(0), 0), new VectSharp.GradientStop(highlightCol, 1)); // Gradient colour stops. // Fill the highlight rectangle (tagging the rectangle with the node Id makes it selectable). graphics.FillRectangle(leftX, topY, rightX - leftX, bottomY - topY, brush, tag: node.Id); } } // In theory, we should return the coordinates of the top-left and bottom-right corners of the area containing // everything that has been drawn by this script. However, since we are not extending the plot area, in this // case it doesn't matter. Point topLeft = new Point(); Point bottomRight = new Point(); return new Point[] { topLeft, bottomRight }; } } } ```

Let me know if you have any questions!

klcym commented 6 months ago

Wow, that's great!