Beispiel #1
0
 /// <summary>
 /// Initializes the visual tree and pool of boundary objects.
 /// </summary>
 public void AttachVisualTree([NotNull] BoxTree tree)
 {
     while (m_pooledBoundaries.Count < tree.Depth)
     {
         m_pooledBoundaries.Push(new Boundary());
     }
 }
        /// <summary>
        /// Computes bounding rectangle in diagram space using only visible (non-autogenerated boxes).
        /// Useful for rendering the chart, as boxes frequently go into negative side horizontally, and have a special root box on top - all of those should not be accounted for.
        /// </summary>
        public static Rect ComputeBranchVisualBoundingRect([NotNull] BoxTree visualTree)
        {
            var result      = new Rect();
            var initialized = false;

            visualTree.Root.IterateParentFirst(node =>
            {
                var box = node.Element;

                if (!node.State.IsHidden && !box.IsSpecial)
                {
                    if (initialized)
                    {
                        result += new Rect(node.State.TopLeft, node.State.Size);
                    }
                    else
                    {
                        initialized = true;
                        result      = new Rect(node.State.TopLeft, node.State.Size);
                    }
                }

                return(!box.IsCollapsed);
            });

            return(result);
        }
Beispiel #3
0
        /// <summary>
        /// Constructs a new tree.
        /// </summary>
        public static BoxTree Build([NotNull] LayoutState state)
        {
            var result = new BoxTree();

            // build dictionary of nodes
            foreach (var box in state.Diagram.Boxes.BoxesById.Values)
            {
                var node = new Node(box);
                result.Nodes.Add(box.Id, node);
            }

            // build the tree
            foreach (var node in result.Nodes.Values)
            {
                var parentKey = node.Element.ParentId;

                Node parentNode;
                if (result.Nodes.TryGetValue(parentKey, out parentNode))
                {
                    if (node.Element.IsAssistant && parentNode.Element.ParentId != Box.None)
                    {
                        parentNode.AddAssistantChild(node);
                    }
                    else
                    {
                        parentNode.AddRegularChild(node);
                    }
                }
                else
                {
                    if (result.Root != null)
                    {
                        throw new InvalidOperationException("More then one root found: " + node.Element.Id);
                    }
                    // In case of data errors, parent key may be not null, but parent node is not there.
                    // Just add the node to roots.
                    result.Root = node;
                }
            }

            return(result);
        }
        private static void RouteConnectors([NotNull] LayoutState state, [NotNull] BoxTree visualTree)
        {
            visualTree.IterateParentFirst(node =>
            {
                if (node.Element.IsCollapsed || node.State.NumberOfSiblings == 0 && node.AssistantsRoot == null)
                {
                    return(false);
                }

                if (node.Level == 0)
                {
                    return(true);
                }

                if (!node.Element.IsSpecial || node.IsAssistantRoot)
                {
                    node.State.RequireLayoutStrategy().RouteConnectors(state, node);
                    return(true);
                }

                return(false);
            });
        }
        /// <summary>
        /// Initializes <paramref name="state"/> and performs all layout operations.
        /// </summary>
        public static void Apply([NotNull] LayoutState state)
        {
            // verify the root
            if (state.Diagram.Boxes.SystemRoot == null)
            {
                throw new InvalidOperationException("SystemRoot is not initialized on the box container");
            }

            state.CurrentOperation = LayoutState.Operation.Preparing;

            var tree = BoxTree.Build(state);

            state.Diagram.VisualTree = tree;

            // verify the root: regardless of data items, there must be a system root box on top of everything
            // the corresponding node is not supposed to be rendered, it only serves as layout algorithm's starting point
            if (tree.Root == null || tree.Root.Element.Id != state.Diagram.Boxes.SystemRoot.Id)
            {
                throw new Exception("SystemRoot is not on the top of the visual tree");
            }

            // set the tree and update visibility
            tree.UpdateHierarchyStats();
            state.AttachVisualTree(tree);

            // update visibility of boxes based on collapsed state
            tree.IterateParentFirst(
                node =>
            {
                node.State.IsHidden =
                    node.ParentNode != null &&
                    (node.ParentNode.State.IsHidden || node.ParentNode.Element.IsCollapsed);

                return(true);
            });

            // In this phase, we will figure out layout strategy
            // and initialize layout state for each node.
            // Event listener may perform initial rendering /measuring of boxes when this event fires,
            // to determine box sizes and be ready to supply them via BoxSizeFunc delegate.
            state.CurrentOperation = LayoutState.Operation.PreprocessVisualTree;

            // initialize box sizes
            if (state.BoxSizeFunc != null)
            {
                // apply box sizes
                foreach (var box in state.Diagram.Boxes.BoxesById.Values.Where(x => x.IsDataBound))
                {
                    box.Size = state.BoxSizeFunc(box.DataId);
                }
            }

            foreach (var box in state.Diagram.Boxes.BoxesById.Values)
            {
                AssertBoxSize(box);
            }

            // initialize layout state on each node
            tree.IterateParentFirst(
                node =>
            {
                node.State.MoveTo(0, 0);
                node.State.Size           = node.Element.Size;
                node.State.BranchExterior = new Rect(new Point(0, 0), node.Element.Size);

                return(true);
            });

            PreprocessVisualTree(state, tree);
            tree.UpdateHierarchyStats();

            state.CurrentOperation = LayoutState.Operation.VerticalLayout;
            VerticalLayout(state, tree.Root);

            state.CurrentOperation = LayoutState.Operation.HorizontalLayout;
            HorizontalLayout(state, tree.Root);

            state.CurrentOperation = LayoutState.Operation.ConnectorsLayout;
            RouteConnectors(state, tree);

            state.CurrentOperation = LayoutState.Operation.Completed;
        }
        private static void PreprocessVisualTree([NotNull] LayoutState state, [NotNull] BoxTree visualTree)
        {
            var defaultStrategy           = state.Diagram.LayoutSettings.RequireDefaultLayoutStrategy();
            var defaultAssistantsStrategy = state.Diagram.LayoutSettings.RequireDefaultAssistantLayoutStrategy();

            var regular = new Stack <LayoutStrategyBase>();

            regular.Push(defaultStrategy);
            var assistants = new Stack <LayoutStrategyBase>();

            assistants.Push(defaultAssistantsStrategy);

            visualTree.IterateParentFirst(node =>
            {
                if (node.State.IsHidden)
                {
                    return(false);
                }

                LayoutStrategyBase strategy = null;

                if (state.LayoutOptimizerFunc != null)
                {
                    var suggestedStrategyId = state.LayoutOptimizerFunc(node);
                    if (!string.IsNullOrEmpty(suggestedStrategyId))
                    {
                        strategy = state.Diagram.LayoutSettings.LayoutStrategies[suggestedStrategyId];
                    }
                }

                if (node.IsAssistantRoot)
                {
                    if (strategy == null)
                    {
                        strategy = node.ParentNode.Element.AssistantLayoutStrategyId != null
                            ? state.Diagram.LayoutSettings.LayoutStrategies[
                            node.ParentNode.Element.AssistantLayoutStrategyId]
                            : assistants.Peek();
                    }
                    assistants.Push(strategy);
                }
                else
                {
                    if (strategy == null)
                    {
                        strategy = node.Element.LayoutStrategyId != null
                            ? state.Diagram.LayoutSettings.LayoutStrategies[node.Element.LayoutStrategyId]
                            : regular.Peek();
                    }
                    regular.Push(strategy);

                    if (!strategy.SupportsAssistants)
                    {
                        node.SuppressAssistants();
                    }
                }

                // now let it pre-allocate special boxes etc
                node.State.EffectiveLayoutStrategy = strategy;
                node.State.RequireLayoutStrategy().PreProcessThisNode(state, node);

                return((!node.Element.IsCollapsed && node.ChildCount > 0) || node.AssistantsRoot != null);
            },
                                          node =>
            {
                if (!node.State.IsHidden)
                {
                    if (node.IsAssistantRoot)
                    {
                        assistants.Pop();
                    }
                    else
                    {
                        regular.Pop();
                    }
                }
            });
        }