/// <summary>
        /// Aggregates the <paramref name="aggregate"/> as well as all its children recursively.
        /// </summary>
        /// <remarks>
        /// Can be used to apply the initial aggregation. If this is not the initial aggregation run, it will reuse existing
        /// aggregation nodes.
        /// </remarks>
        /// <param name="aggregate">The "root" aggregate.</param>
        /// <returns>The aggregation node representing the passed <paramref name="aggregate"/></returns>
        public INode AggregateRecursively(NodeAggregation.Aggregate aggregate)
        {
            if (aggregate.Children.Count == 0)
            {
                return(aggregate.Node);
            }

            PointD originalCenter;

            if (aggregateToNode.TryGetValue(aggregate, out var node))
            {
                originalCenter = node.Layout.GetCenter();
                var aggregationInfo = (AggregationNodeInfo)node.Tag;
                if (aggregationInfo.IsAggregated)
                {
                    return(node);
                }
                else
                {
                    AggregateGraph.Separate(node);
                }
            }
            else
            {
                originalCenter = PointD.Origin;
            }

            var nodesToAggregate = aggregate.Children.Select(AggregateRecursively).ToList();

            if (aggregate.Node != null)
            {
                nodesToAggregate.Add(aggregate.Node);
            }

            var size            = 30 + Math.Sqrt(aggregate.DescendantWeightSum) * 4;
            var layout          = RectD.FromCenter(originalCenter, new SizeD(size, size));
            var aggregationNode =
                AggregateGraph.Aggregate(new ListEnumerable <INode>(nodesToAggregate), layout, AggregationNodeStyle);

            aggregateToNode[aggregate] = aggregationNode;
            aggregationNode.Tag        = new AggregationNodeInfo(aggregate, true);

            if (aggregate.Node != null)
            {
                placeholderMap[aggregate.Node] = aggregationNode;
                CopyLabels(aggregate.Node, aggregationNode);
            }
            else
            {
                var maxChild = GetMostImportantDescendant(aggregate);
                if (maxChild.Node != null && maxChild.Node.Labels.Any())
                {
                    AggregateGraph.AddLabel(aggregationNode, $"({maxChild.Node.Labels[0].Text}, …)",
                                            FreeNodeLabelModel.Instance.CreateDefaultParameter(), DescendantLabelStyle);
                }
            }

            return(aggregationNode);
        }
 /// <summary>
 /// Gets the descendant <see cref="NodeAggregation.Aggregate"/> with the highest
 /// <see cref="NodeAggregation.Aggregate.DescendantWeightSum"/>.
 /// </summary>
 private static NodeAggregation.Aggregate GetMostImportantDescendant(NodeAggregation.Aggregate aggregate)
 {
     while (true)
     {
         var maxChild = aggregate.Children.Aggregate((max, child) =>
                                                     child.DescendantWeightSum > max.DescendantWeightSum ? child : max);
         if (maxChild.Node != null)
         {
             return(maxChild);
         }
         aggregate = maxChild;
     }
 }
 public AggregationNodeInfo(NodeAggregation.Aggregate aggregate, bool isAggregated)
 {
     this.Aggregate    = aggregate;
     this.IsAggregated = isAggregated;
 }