private GroupLayoutInfo LayoutRecursive(
            [NotNull] DiagramLayoutStructure layoutStructure,
            [NotNull] ILayoutGroup layoutGroup,
            [NotNull] IGroupLayoutAlgorithm layoutAlgorithm)
        {
            var childLayoutByParentNodeId = new Dictionary <ModelNodeId, GroupLayoutInfo>();

            foreach (var node in layoutGroup.Nodes)
            {
                var maybeLayoutGroup = layoutStructure.TryGetLayoutGroupByNodeId(node.Id);
                if (!maybeLayoutGroup.HasValue || maybeLayoutGroup.Value.IsEmpty)
                {
                    continue;
                }

                var nodeLayoutAlgorithm    = _layoutAlgorithmSelectionStrategy.GetForNode(node);
                var childrenAreaLayoutInfo = LayoutRecursive(layoutStructure, maybeLayoutGroup.Value, nodeLayoutAlgorithm);

                layoutGroup.SetChildrenAreaSize(node.Id, childrenAreaLayoutInfo.Rect.Size);
                childLayoutByParentNodeId.Add(node.Id, childrenAreaLayoutInfo);
            }

            var groupLayoutInfo = layoutAlgorithm.Calculate(layoutGroup);

            foreach (var nodeLayoutInfo in groupLayoutInfo.Nodes)
            {
                if (childLayoutByParentNodeId.TryGetValue(nodeLayoutInfo.Node.Id, out var childrenAreaLayoutInfo))
                {
                    nodeLayoutInfo.ChildrenArea = childrenAreaLayoutInfo;
                }
            }

            return(groupLayoutInfo);
        }
        public DiagramLayoutInfo Calculate(IDiagram diagram)
        {
            var layoutStructure     = new DiagramLayoutStructure(diagram);
            var rootLayoutAlgorithm = _layoutAlgorithmSelectionStrategy.GetForRoot();
            var rootLayoutInfo      = LayoutRecursive(layoutStructure, layoutStructure.RootLayoutGroup, rootLayoutAlgorithm);

            return(new DiagramLayoutInfo(rootLayoutInfo.Nodes, rootLayoutInfo.Connectors));
        }
        public GroupLayoutInfo Calculate(IDiagram diagram)
        {
            var layoutStructure = new DiagramLayoutStructure(diagram);

            var rootLayoutAlgorithm = _layoutAlgorithmSelectionStrategy.GetForRoot();

            var relativeLayout = LayoutRecursive(layoutStructure, layoutStructure.RootLayoutGroup, rootLayoutAlgorithm);

            var absoluteLayout = _layoutUnifier.CalculateAbsoluteLayout(relativeLayout);

            var crossGroupLines = CreateDirectRoutes(layoutStructure.CrossLayoutGroupConnectors, diagram, absoluteLayout);

            return(absoluteLayout.AddLineLayoutInfo(crossGroupLines));
        }
        private GroupLayoutInfo LayoutRecursive(
            [NotNull] DiagramLayoutStructure layoutStructure,
            [NotNull] ILayoutGroup layoutGroup,
            [NotNull] IGroupLayoutAlgorithm layoutAlgorithm)
        {
            var childLayoutByParentNodeId = new Dictionary <string, GroupLayoutInfo>();

            foreach (var node in layoutGroup.Nodes)
            {
                var maybeLayoutGroup = layoutStructure.TryGetLayoutGroupByNodeId(node.Id);
                if (!maybeLayoutGroup.HasValue || maybeLayoutGroup.Value.IsEmpty)
                {
                    continue;
                }

                var nodeLayoutAlgorithm    = _layoutAlgorithmSelectionStrategy.GetForNode(node);
                var childrenAreaLayoutInfo = LayoutRecursive(layoutStructure, maybeLayoutGroup.Value, nodeLayoutAlgorithm);

                layoutGroup.SetChildrenAreaSize(node.Id, childrenAreaLayoutInfo.Rect.Size.WithMargin(ChildrenAreaPadding));
                childLayoutByParentNodeId.Add(node.ShapeId, childrenAreaLayoutInfo);
            }

            var layoutInfo = layoutAlgorithm.Calculate(layoutGroup);

            var boxes = layoutGroup.Nodes.Select(
                i =>
            {
                childLayoutByParentNodeId.TryGetValue(i.ShapeId, out var childrenAreaLayoutInfo);

                return(new BoxLayoutInfo(
                           i.ShapeId,
                           layoutInfo.VertexRects[i.Id].TopLeft,
                           payloadAreaSize: i.PayloadAreaSize,
                           childrenAreaLayoutInfo?.Rect.Size.WithMargin(ChildrenAreaPadding) ?? Size2D.Zero,
                           childrenAreaLayoutInfo));
            }
                );

            var lines = layoutGroup.Connectors.Select(i => new LineLayoutInfo(i.ShapeId, layoutInfo.EdgeRoutes[i.Id]));

            return(new GroupLayoutInfo(boxes, lines));
        }