/// <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>
 /// Separates all <paramref name="nodes"/> and runs the layout afterwards.
 /// </summary>
 private void Separate(IEnumerable <INode> nodes)
 {
     foreach (var child in nodes)
     {
         if (AggregateGraph.IsAggregationItem(child))
         {
             AggregateGraph.Separate(child);
         }
     }
     RunLayout();
 }
        /// <summary>
        /// Separates an aggregated aggregation node and replaces it by a new aggregation node.
        /// </summary>
        /// <remarks>
        /// Creates hierarchy edges between the new aggregation node and its children.
        /// </remarks>
        /// <param name="node">The node.</param>
        /// <returns>The nodes affected by this operation. The created aggregation node is always the first item.</returns>
        public IListEnumerable <INode> Separate(INode node)
        {
            var aggregationInfo = (AggregationNodeInfo)node.Tag;
            var aggregate       = aggregationInfo.Aggregate;
            var aggregatedItems = AggregateGraph.GetAggregatedItems(node)
                                  .Where(n => n != aggregate.Node)
                                  .Cast <INode>().ToList();

            AggregateGraph.Separate(node);

            var nodesToAggregate = aggregate.Node != null
          ? new ListEnumerable <INode>(new List <INode> {
                aggregate.Node
            })
          : ListEnumerable <INode> .Empty;
            var aggregationNode = AggregateGraph.Aggregate(nodesToAggregate, node.Layout.ToRectD(), AggregationNodeStyle);

            foreach (var child in aggregatedItems)
            {
                AggregateGraph.CreateEdge(aggregationNode, child, HierarchyEdgeStyle, true);
                AggregateGraph.SetNodeLayout(child,
                                             RectD.FromCenter(aggregationNode.Layout.GetCenter(), child.Layout.ToSizeD()));
                ReplaceEdges(child);
            }

            aggregationInfo.IsAggregated = false;
            aggregateToNode[aggregate]   = aggregationNode;
            aggregationNode.Tag          = aggregationInfo;

            var affectedNodes = new List <INode> {
                aggregationNode
            };

            affectedNodes.AddRange(aggregatedItems);

            if (aggregate.Parent != null && aggregateToNode.TryGetValue(aggregate.Parent, out var parentNode))
            {
                AggregateGraph.CreateEdge(parentNode, aggregationNode, HierarchyEdgeStyle, true);
                affectedNodes.Add(parentNode);
            }

            if (aggregate.Node != null)
            {
                placeholderMap[aggregate.Node] = aggregationNode;
                CopyLabels(aggregate.Node, aggregationNode);
                ReplaceEdges(aggregationNode);
            }

            return(new ListEnumerable <INode>(affectedNodes));
        }