// 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); } } }
public static async Task <TreeNode <NodeData> > GenerateTreeAsync(Species species, AncestryTreeGenerationFlags flags) { // Start by finding the earliest ancestor of this species. List <long> ancestor_ids = new List <long>(); if (!flags.HasFlag(AncestryTreeGenerationFlags.DescendantsOnly)) { ancestor_ids.AddRange(await SpeciesUtils.GetAncestorIdsAsync(species.Id)); } ancestor_ids.Add(species.Id); // Starting from the earliest ancestor, generate all tiers, down to the latest descendant. TreeNode <NodeData> root = new TreeNode <NodeData> { Value = new NodeData { Species = await SpeciesUtils.GetSpeciesAsync(ancestor_ids.First()), IsAncestor = true } }; Queue <TreeNode <NodeData> > queue = new Queue <TreeNode <NodeData> >(); queue.Enqueue(root); while (queue.Count() > 0) { Species[] descendants = await SpeciesUtils.GetDirectDescendantsAsync(queue.First().Value.Species); foreach (Species descendant in descendants) { TreeNode <NodeData> node = new TreeNode <NodeData> { Value = new NodeData { Species = descendant, IsAncestor = ancestor_ids.Contains(descendant.Id) } }; if (!flags.HasFlag(AncestryTreeGenerationFlags.AncestorsOnly) || node.Value.IsAncestor) { queue.First().AddChild(node); queue.Enqueue(node); } } queue.Dequeue(); } return(root); }