コード例 #1
0
        /// <inheritdoc />
        protected override ILayoutAlgorithm CreateConfiguredLayout(GraphControl graphControl)
        {
            var transformer = new GraphTransformer();

            transformer.Operation             = OperationItem;
            transformer.SubgraphLayoutEnabled = ActOnSelectionOnlyItem;
            transformer.RotationAngle         = RotationAngleItem;
            if (ApplyBestFitRotationItem && OperationItem == OperationType.Rotate)
            {
                var size = graphControl.InnerSize;
                ApplyBestFitRotationItem = true;

                var layoutGraph = new LayoutGraphAdapter(graphControl.Graph).CreateCopiedLayoutGraph();
                transformer.RotationAngle = GraphTransformer.FindBestFitRotationAngle(layoutGraph, size.Width, size.Height);
            }
            else
            {
                ApplyBestFitRotationItem = false;
            }

            transformer.ScaleFactor   = ScaleFactorItem;
            transformer.ScaleNodeSize = ScaleNodeSizeItem;
            transformer.TranslateX    = TranslateXItem;
            transformer.TranslateY    = TranslateYItem;

            return(transformer);
        }
コード例 #2
0
        /// <summary>
        /// Initializes the graph instance setting default styles
        /// and creating a small sample graph.
        /// </summary>
        protected virtual void InitializeGraph()
        {
            IGraph graph = Graph;

            // load a sample graph
            new GraphMLIOHandler().Read(graph, "Resources/sample.graphml");

            // set some defaults
            graph.NodeDefaults.Style = Enumerable.First(graph.Nodes).Style;
            graph.NodeDefaults.ShareStyleInstance = true;

            // we start with a simple run of OrganicLayout to get a good starting result
            // the algorithm is optimized to "unfold" graphs quicker than
            // interactive organic, so we use this result as a starting solution
            var initialLayout = new OrganicLayout {
                MinimumNodeDistance = 50
            };

            graph.ApplyLayout(initialLayout);

            // center the initial graph
            GraphControl.FitGraphBounds();

            movedNodes = new List <INode>();

            // we wrap the PositionHandler for nodes so that we always have the collection of nodes
            // that are currently being moved available in "movedNodes".
            // this way we do not need to know how the node is moved and do not have to guess
            // what elements are currently being moved based upon selection, etc.
            graph.GetDecorator().NodeDecorator.PositionHandlerDecorator.SetImplementationWrapper(
                (item, implementation) => new CollectingPositionHandlerWrapper(item, movedNodes, implementation));

            // create a copy of the graph for the layout algorithm
            LayoutGraphAdapter adapter = new LayoutGraphAdapter(graphControl.Graph);

            copiedLayoutGraph = adapter.CreateCopiedLayoutGraph();

            // create and start the layout algorithm
            layout = StartLayout();
            WakeUp();

            // register a listener so that structure updates are handled automatically
            graph.NodeCreated += delegate(object source, ItemEventArgs <INode> args) {
                if (layout != null)
                {
                    var center = args.Item.Layout.GetCenter();
                    layout.SyncStructure(true);
                    //we nail down all newly created nodes
                    var copiedNode = copiedLayoutGraph.GetCopiedNode(args.Item);
                    layout.SetCenter(copiedNode, center.X, center.Y);
                    layout.SetInertia(copiedNode, 1);
                    layout.SetStress(copiedNode, 0);
                    layout.WakeUp();
                }
            };
            graph.NodeRemoved += OnStructureChanged;
            graph.EdgeCreated += OnStructureChanged;
            graph.EdgeRemoved += OnStructureChanged;
        }
コード例 #3
0
        private static void AddNodeHalos(LayoutGraphAdapter adapter, IGraph graph, bool layoutOnlySelection)
        {
            var nodeHalos = new DictionaryMapper <INode, NodeHalo>();

            foreach (var node in graph.Nodes)
            {
                var top    = 0.0;
                var left   = 0.0;
                var bottom = 0.0;
                var right  = 0.0;

                // for each port with an EventPortStyle extend the node halo to cover the ports render size
                foreach (var port in node.Ports)
                {
                    var eventPortStyle = port.Style as EventPortStyle;
                    if (eventPortStyle != null)
                    {
                        var renderSize = eventPortStyle.RenderSize;
                        var location   = port.GetLocation();
                        top    = Math.Max(top, node.Layout.Y - location.Y - renderSize.Height / 2);
                        left   = Math.Max(left, node.Layout.X - location.X - renderSize.Width / 2);
                        bottom = Math.Max(bottom, location.Y + renderSize.Height / 2 - node.Layout.GetMaxY());
                        right  = Math.Max(right, location.X + renderSize.Width / 2 - node.Layout.GetMaxX());
                    }
                }

                // for each node without incoming or outgoing edges reserve space for laid out exterior labels
                if (graph.InDegree(node) == 0 || graph.OutDegree(node) == 0)
                {
                    foreach (var label in node.Labels)
                    {
                        if (IsNodeLabelAffected(label, adapter, layoutOnlySelection))
                        {
                            var labelBounds = label.GetLayout().GetBounds();
                            if (graph.InDegree(node) == 0)
                            {
                                left = Math.Max(left, labelBounds.Width);
                                top  = Math.Max(top, labelBounds.Height);
                            }
                            if (graph.OutDegree(node) == 0)
                            {
                                right  = Math.Max(right, labelBounds.Width);
                                bottom = Math.Max(bottom, labelBounds.Height);
                            }
                        }
                    }
                }

                nodeHalos[node] = NodeHalo.Create(top, left, bottom, right);
            }
            adapter.AddDataProvider(NodeHalo.NodeHaloDpKey, nodeHalos);
        }
コード例 #4
0
        private static bool IsNodeLabelAffected(ILabel label, LayoutGraphAdapter adapter, bool layoutOnlySelection)
        {
            var node = label.Owner as INode;

            if (node != null)
            {
                var  isInnerLabel   = node.Layout.Contains(label.GetLayout().GetCenter());
                bool isPool         = node.Style is PoolNodeStyle;
                bool isChoreography = node.Style is ChoreographyNodeStyle;
                var  isGroupNode    = adapter.AdaptedGraph.IsGroupNode(node);
                return(!isInnerLabel && !isPool && !isChoreography && !isGroupNode && (!layoutOnlySelection || adapter.SelectionModel.IsSelected(node)));
            }
            return(false);
        }
コード例 #5
0
        private static void AddEdgeLabelPlacementDescriptors(LayoutGraphAdapter adapter)
        {
            var atSourceDescriptor = new PreferredPlacementDescriptor {
                PlaceAlongEdge = LabelPlacements.AtSourcePort,
                SideOfEdge     = LabelPlacements.LeftOfEdge | LabelPlacements.RightOfEdge,
            };

            adapter.AddDataProvider(LayoutGraphAdapter.EdgeLabelLayoutPreferredPlacementDescriptorDpKey,
                                    Mappers.FromDelegate((ILabel label) => {
                var edgeType = ((BpmnEdgeStyle)((IEdge)label.Owner).Style).Type;
                if (edgeType == EdgeType.SequenceFlow || edgeType == EdgeType.DefaultFlow || edgeType == EdgeType.ConditionalFlow)
                {
                    // labels on sequence, default and conditional flow edges should be placed at the source side.
                    return(atSourceDescriptor);
                }
                return(null);
            }));
        }
コード例 #6
0
        protected override void Apply(LayoutGraphAdapter adapter, ILayoutAlgorithm layout, CopiedLayoutGraph layoutGraph)
        {
            var graph = adapter.AdaptedGraph;

            // check if only selected elements should be laid out
            var layoutOnlySelection = layout is BpmnLayout && ((BpmnLayout)layout).Scope == Scope.SelectedElements;

            // mark 'flow' edges, i.e. sequence flows, default flows and conditional flows
            adapter.AddDataProvider(BpmnLayout.SequenceFlowEdgesDpKey, Mappers.FromDelegate <IEdge, bool>(IsSequenceFlow));

            // mark boundary interrupting edges for the BalancingPortOptimizer
            adapter.AddDataProvider(BpmnLayout.BoundaryInterruptingEdgesDpKey, Mappers.FromDelegate((IEdge edge) => edge.SourcePort.Style is EventPortStyle));

            // mark conversations, events and gateways so their port locations are adjusted
            adapter.AddDataProvider(PortLocationAdjuster.AffectedNodesDpKey,
                                    Mappers.FromDelegate((INode node) => (node.Style is ConversationNodeStyle || node.Style is EventNodeStyle || node.Style is GatewayNodeStyle)));

            // add NodeHalos around nodes with event ports or specific exterior labels so the layout keeps space for the event ports and labels as well
            AddNodeHalos(adapter, graph, layoutOnlySelection);

            // add PreferredPlacementDescriptors for labels on sequence, default or conditional flows to place them at source side
            AddEdgeLabelPlacementDescriptors(adapter);

            // mark nodes, edges and labels as either fixed or affected by the layout and configure port constraints and incremental hints
            MarkFixedAndAffectedItems(adapter, layoutOnlySelection);

            // mark associations and message flows as undirected so they have less impact on layering
            EdgeDirectedness.Delegate = edge => (IsMessageFlow(edge) || IsAssociation(edge)) ? 0 : 1;

            // add layer constraints for start events, sub processes and message flows
            AddLayerConstraints(graph);

            // add EdgeLayoutDescriptor to specify minimum edge length for edges
            AddMinimumEdgeLength(MinimumEdgeLength);

            base.Apply(adapter, layout, layoutGraph);
        }
コード例 #7
0
 protected override void Apply(LayoutGraphAdapter layoutGraphAdapter, ILayoutAlgorithm layout, CopiedLayoutGraph layoutGraph)
 {
     layoutGraphAdapter.AddDataProvider(SelectedNodes.DpKey, SelectedNodes.ProvideMapper(layoutGraphAdapter, layout));
 }
コード例 #8
0
        /// <summary>
        /// Executes the module on the given graph using the provided context.
        /// </summary>
        /// <remarks>
        /// The layout will be calculated <see cref="RunInBackground">optionally</see>
        /// in a separate thread in method <see cref="RunModuleAsync"/>.
        /// </remarks>
        /// <param name="graph">The graph to execute on.</param>
        /// <param name="newContext">The context to use. This method will query a <c>ISelectionModel&lt;IModelItem></c></param>
        /// for the selected nodes and edges and the <c>GraphControl</c> to morph the layout.
        protected virtual async Task StartWithIGraph(IGraph graph, ILookup newContext)
        {
            this.graph = graph;
            if (ShouldConfigureTableLayout())
            {
                PrepareTableLayout();
            }
            ISelectionModel <IModelItem> selectionModel = newContext.Lookup <ISelectionModel <IModelItem> >();
            LayoutGraphAdapter           adapter        = new LayoutGraphAdapter(graph, selectionModel);

            this.layoutGraph = adapter.CreateCopiedLayoutGraph();
            ILookup additionalLookup = Lookups.Single(layoutGraph, typeof(LayoutGraph));
            ILookup wrappedLookup    = Lookups.Wrapped(newContext, additionalLookup);

            try {
                ICompoundEdit compoundEdit = graph.BeginEdit("Layout", "Layout");
                CheckReentrant(wrappedLookup);
                ConfigureModule();

                if (RunInBackground)
                {
                    // without the LayoutExecutor helper class on the layout graph side of things, we register the aborthandler
                    // to the layout graph with the utility method provided by AbortHandler
                    var abortHandler = AbortHandler.CreateForGraph(layoutGraph);
                    // now create the dialog that controls the abort handler
                    abortDialog = new AbortDialog {
                        AbortHandler = abortHandler, Owner = Application.Current.MainWindow
                    };
                    // start the layout in another thread.
                    var layoutThread = new Thread(async() => await RunModuleAsync(wrappedLookup, graph, compoundEdit));

                    // now if we are not doing a quick layout - and if it takes more than a few seconds, we open the dialog to
                    // enable the user to stop or cancel the execution
                    var showDialogTimer = new DispatcherTimer(DispatcherPriority.Normal, abortDialog.Dispatcher)
                    {
                        Interval = TimeSpan.FromSeconds(2)
                    };

                    showDialogTimer.Tick += delegate {
                        // it could be that the layout is already done - so check whether we still
                        // need to open the dialog
                        var dialogInstance = abortDialog;
                        if (dialogInstance != null)
                        {
                            // open the abort dialog
                            dialogInstance.Show();
                        }
                        // we only want to let it go off once - so stop the timer
                        showDialogTimer.Stop();
                    };

                    // kick-off the timer and the layout
                    showDialogTimer.Start();
                    layoutThread.Start();
                }
                else
                {
                    await RunModuleAsync(wrappedLookup, graph, compoundEdit);
                }
            } catch (Exception e) {
                FreeReentrant();
                TableLayoutConfigurator.CleanUp(graph);
                OnDone(new LayoutEventArgs(e));
                //optionally do something here...
            }
        }
コード例 #9
0
        private void MarkFixedAndAffectedItems(LayoutGraphAdapter adapter, bool layoutOnlySelection)
        {
            if (layoutOnlySelection)
            {
                var affectedEdges = Mappers.FromDelegate((IEdge edge) =>
                                                         adapter.SelectionModel.IsSelected(edge) ||
                                                         adapter.SelectionModel.IsSelected(edge.GetSourceNode()) ||
                                                         adapter.SelectionModel.IsSelected(edge.GetTargetNode()));
                adapter.AddDataProvider(LayoutKeys.AffectedEdgesDpKey, affectedEdges);

                // fix ports of unselected edges and edges at event ports
                adapter.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, Mappers.FromDelegate <IEdge, PortConstraint>(
                                            edge =>
                                            (!affectedEdges[edge] || edge.SourcePort.Style is EventPortStyle)
              ? PortConstraint.Create(GetSide(edge, true))
              : null));
                adapter.AddDataProvider(PortConstraintKeys.TargetPortConstraintDpKey, Mappers.FromDelegate <IEdge, PortConstraint>(
                                            edge => !affectedEdges[edge] ? PortConstraint.Create(GetSide(edge, false)) : null));

                // give core layout hints that selected nodes and edges should be incremental
                IncrementalHints.ContextDelegate = (item, factory) => {
                    if (item is INode && adapter.SelectionModel.IsSelected(item))
                    {
                        return(factory.CreateLayerIncrementallyHint(item));
                    }
                    else if (item is IEdge && affectedEdges[(IEdge)item])
                    {
                        return(factory.CreateSequenceIncrementallyHint(item));
                    }
                    return(null);
                };
                adapter.AddDataProvider(BpmnLayout.AffectedLabelsDpKey, Mappers.FromDelegate <ILabel, bool>(label => {
                    var edge = label.Owner as IEdge;
                    if (edge != null)
                    {
                        return(affectedEdges[edge]);
                    }
                    var node = label.Owner as INode;
                    if (node != null)
                    {
                        var isInnerLabel    = node.Layout.Contains(label.GetLayout().GetCenter());
                        bool isPool         = node.Style is PoolNodeStyle;
                        bool isChoreography = node.Style is ChoreographyNodeStyle;
                        return(!isInnerLabel && !isPool && !isChoreography && adapter.SelectionModel.IsSelected(node));
                    }
                    return(false);
                }));
            }
            else
            {
                // fix source port of edges at event ports
                adapter.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, Mappers.FromDelegate <IEdge, PortConstraint>(
                                            edge => edge.SourcePort.Style is EventPortStyle ? PortConstraint.Create(GetSide(edge, true)) : null));

                adapter.AddDataProvider(BpmnLayout.AffectedLabelsDpKey, Mappers.FromDelegate <ILabel, bool>(label => {
                    if (label.Owner is IEdge)
                    {
                        return(true);
                    }
                    var node = label.Owner as INode;
                    if (node != null)
                    {
                        var isInnerLabel    = node.Layout.Contains(label.GetLayout().GetCenter());
                        bool isPool         = node.Style is PoolNodeStyle;
                        bool isChoreography = node.Style is ChoreographyNodeStyle;
                        return(!isInnerLabel && !isPool && !isChoreography);
                    }
                    return(false);
                }));
            }
        }
コード例 #10
0
 protected override void Apply(LayoutGraphAdapter layoutGraphAdapter, ILayoutAlgorithm layout, CopiedLayoutGraph layoutGraph)
 {
     layoutGraphAdapter.AddDataProvider(SelectedLabelsStage.SelectedLabelsAtItemKey, SelectedLabelsAtItem.ProvideMapper(layoutGraphAdapter, layout));
 }
 private void DoLayout()
 {
     LayoutGraphAdapter.ApplyLayout(Graph, layout);
     graphControl.FitGraphBounds();
 }