Esempio n. 1
0
 private static void _shiftTree(TreeNode <AncestryTreeRendererNodeData> root, float deltaX, float deltaY)
 {
     TreeUtils.PostOrderTraverse(root, (node) => {
         node.Value.Bounds.X += deltaX;
         node.Value.Bounds.Y += deltaY;
     });
 }
Esempio n. 2
0
        // Public members

        public static async Task <string> Save(Species species, AncestryTreeGenerationFlags flags)
        {
            // Generate the ancestry tree.

            TreeNode <AncestryTree.NodeData> ancestry_tree_root = await AncestryTree.GenerateTreeAsync(species, flags);

            TreeNode <AncestryTreeRendererNodeData> root = TreeUtils.CopyAs(ancestry_tree_root, x => {
                return(new AncestryTreeRendererNodeData {
                    Species = x.Value.Species,
                    IsAncestor = x.Value.IsAncestor,
                    Bounds = new RectangleF()
                });
            });

            // Generate the evolution tree image.

            using (Font font = new Font("Calibri", 12)) {
                // Calculate the size of each node.

                float horizontal_padding = 5.0f;

                TreeUtils.PostOrderTraverse(root, (node) => {
                    SizeF size = GraphicsUtils.MeasureString(node.Value.Species.ShortName, font);

                    node.Value.Bounds.Width  = size.Width + horizontal_padding;
                    node.Value.Bounds.Height = size.Height;
                });

                // Calculate node placements.

                _calculateNodePlacements(root);

                // Calculate the size of the tree.

                RectangleF bounds = _calculateTreeBounds(root);

                // Shift the tree so that the entire thing is visible.

                float min_x = 0.0f;

                TreeUtils.PostOrderTraverse(root, (node) => {
                    if (node.Value.Bounds.X < min_x)
                    {
                        min_x = bounds.X;
                    }
                });

                _shiftTree(root, -min_x, 0.0f);

                // Create the bitmap.

                using (Bitmap bmp = new Bitmap((int)bounds.Width, (int)bounds.Height))
                    using (Graphics gfx = Graphics.FromImage(bmp)) {
                        gfx.Clear(Color.FromArgb(54, 57, 63));
                        gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                        gfx.SmoothingMode     = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

                        _drawSpeciesTreeNode(gfx, root, species, font);

                        // Save the result.

                        string out_dir = Global.TempDirectory + "anc";

                        if (!System.IO.Directory.Exists(out_dir))
                        {
                            System.IO.Directory.CreateDirectory(out_dir);
                        }

                        string fpath = System.IO.Path.Combine(out_dir, species.ShortName + ".png");

                        bmp.Save(fpath);

                        return(fpath);
                    }
            }
        }
Esempio n. 3
0
        private static void _fixSubTreeOverlap(TreeNode <AncestryTreeRendererNodeData> node)
        {
            TreeUtils.PostOrderTraverse(node, (n) => {
                // Check if this node has overlapping subtrees.

                if (n.Children.Count() <= 1)
                {
                    return;
                }

                bool has_overlapping = false;
                List <NodeBoundsPair <AncestryTreeRendererNodeData> > bounds = new List <NodeBoundsPair <AncestryTreeRendererNodeData> >();

                foreach (TreeNode <AncestryTreeRendererNodeData> child in node.Children)
                {
                    bounds.Add(new NodeBoundsPair <AncestryTreeRendererNodeData> {
                        Node   = child,
                        Bounds = _calculateTreeBounds(child)
                    });
                }

                for (int i = 1; i < bounds.Count(); ++i)
                {
                    if (bounds[i].Bounds.Left < bounds[i - 1].Bounds.Right)
                    {
                        has_overlapping = true;

                        break;
                    }
                }

                // If there are overlapping subtrees, move them so that they are no longer overlapping.

                if (has_overlapping)
                {
                    int left  = 0;
                    int right = 0;

                    if (bounds.Count() % 2 == 1)
                    {
                        int middle = (int)Math.Ceiling(bounds.Count() / 2.0f);

                        left  = middle - 1;
                        right = middle + 1;
                    }
                    else
                    {
                        left  = (bounds.Count() / 2) - 1;
                        right = bounds.Count() / 2;
                    }

                    for (int i = left; i >= 0; --i)
                    {
                        float overlap = bounds[i].Bounds.Right - bounds[i + 1].Bounds.Left;

                        if (overlap <= 0.0f)
                        {
                            continue;
                        }

                        if (i == left)
                        {
                            overlap /= 2.0f;
                        }

                        bounds[i].Bounds.X -= overlap;
                        _shiftTree(bounds[i].Node, -overlap, 0.0f);
                    }

                    for (int i = right; i < bounds.Count(); ++i)
                    {
                        float overlap = bounds[i - 1].Bounds.Right - bounds[i].Bounds.Left;

                        if (overlap <= 0.0f)
                        {
                            continue;
                        }

                        if (i == right)
                        {
                            overlap /= 2.0f;
                        }

                        bounds[i].Bounds.X += overlap;
                        _shiftTree(bounds[i].Node, overlap, 0.0f);
                    }
                }

                // Finally, center the parent node over its children.

                float child_min_x = node.Children.First().Value.Bounds.X;
                float child_max_x = node.Children.Last().Value.Bounds.X + node.Children.Last().Value.Bounds.Width;
                float child_width = (child_max_x - child_min_x);

                node.Value.Bounds.X = child_min_x + (child_width / 2.0f) - (node.Value.Bounds.Width / 2.0f);
            });
        }
        // Private members

        private string _treeToString()
        {
            // If no tree has been provided, there is nothing to render.

            if (Tree is null)
            {
                return(string.Empty);
            }

            // Generate timestamp strings for each entry ahead of time, so we can make sure that they're all the same length.

            int maxTimestampLength = 0;

            TreeUtils.PreOrderTraverse(Tree, x => {
                int length = _timestampToString(x.Value.Species.Timestamp).Length;

                if (length > maxTimestampLength)
                {
                    maxTimestampLength = length;
                }
            });

            // Render the tree.

            List <string>             lines = new List <string>();
            Stack <Tuple <int, int> > sibling_line_positions = new Stack <Tuple <int, int> >();

            TreeUtils.PreOrderTraverse(Tree, x => {
                string line = "";

                line += _timestampToString(x.Value.Species.Timestamp).PadRight(maxTimestampLength);
                line += " " + (x.Value.Species.IsExtinct ? "*" : "-");

                if (DrawLines && x.Parent != null)
                {
                    for (int i = 0; i < x.Depth * 2 - 1; ++i)
                    {
                        if (sibling_line_positions.Count() > 0 && sibling_line_positions.Any(y => y.Item2 == line.Length + 1))
                        {
                            line += "│";
                        }
                        else
                        {
                            line += " ";
                        }
                    }
                }

                else
                {
                    line += " ";
                }

                if (x.Parent != null)
                {
                    // If this node has a parent, draw a branch leading down it.

                    if (x.Parent.Children.Count() > 1 && x.Parent.Children.Last() != x)
                    {
                        if (DrawLines)
                        {
                            line += "├─";
                        }

                        // If this is the first sibling, take note of the index to draw connecting lines.

                        if (sibling_line_positions.Count() == 0 || sibling_line_positions.First().Item1 != x.Depth)
                        {
                            sibling_line_positions.Push(new Tuple <int, int>(x.Depth, line.Length - 1));
                        }
                    }
                    else if (x.Parent.Children.Last() == x)
                    {
                        if (DrawLines)
                        {
                            line += "└─";
                        }

                        // If this is the last sibling, remove the stored index.

                        if (sibling_line_positions.Count() > 0 && sibling_line_positions.First().Item1 == x.Depth)
                        {
                            sibling_line_positions.Pop();
                        }
                    }
                }

                line += x.Value.Species.ShortName;

                lines.Add(line);
            });

            StringBuilder sb = new StringBuilder();

            foreach (string line in lines)
            {
                if (sb.Length + line.Length > MaxLength)
                {
                    sb.AppendLine("...");

                    break;
                }
                else
                {
                    sb.AppendLine(line);
                }
            }

            return(sb.ToString());
        }